From f160a13c1253b6f06205c6082573b115a68a312c Mon Sep 17 00:00:00 2001 From: Dmitry Kornilov Date: Tue, 9 Jan 2018 22:58:35 +0100 Subject: [PATCH] Initial contribution Signed-off-by: Dmitry Kornilov --- LICENSE | 203 ---- LICENSE.md | 637 ++++++++++++ README.md | 21 + api/pom.xml | 128 +++ api/src/main/java/javax/json/EmptyArray.java | 113 +++ api/src/main/java/javax/json/EmptyObject.java | 102 ++ api/src/main/java/javax/json/Json.java | 520 ++++++++++ api/src/main/java/javax/json/JsonArray.java | 274 ++++++ .../java/javax/json/JsonArrayBuilder.java | 622 ++++++++++++ .../java/javax/json/JsonBuilderFactory.java | 133 +++ .../main/java/javax/json/JsonException.java | 55 ++ .../main/java/javax/json/JsonMergePatch.java | 63 ++ api/src/main/java/javax/json/JsonNumber.java | 186 ++++ api/src/main/java/javax/json/JsonObject.java | 249 +++++ .../java/javax/json/JsonObjectBuilder.java | 283 ++++++ api/src/main/java/javax/json/JsonPatch.java | 139 +++ .../java/javax/json/JsonPatchBuilder.java | 186 ++++ api/src/main/java/javax/json/JsonPointer.java | 116 +++ api/src/main/java/javax/json/JsonReader.java | 133 +++ .../java/javax/json/JsonReaderFactory.java | 91 ++ api/src/main/java/javax/json/JsonString.java | 63 ++ .../main/java/javax/json/JsonStructure.java | 38 + api/src/main/java/javax/json/JsonValue.java | 140 +++ .../main/java/javax/json/JsonValueImpl.java | 83 ++ api/src/main/java/javax/json/JsonWriter.java | 123 +++ .../java/javax/json/JsonWriterFactory.java | 94 ++ .../main/java/javax/json/package-info.java | 45 + .../java/javax/json/spi/JsonProvider.java | 480 +++++++++ .../java/javax/json/spi/package-info.java | 32 + .../javax/json/stream/JsonCollectors.java | 160 +++ .../json/stream/JsonGenerationException.java | 57 ++ .../java/javax/json/stream/JsonGenerator.java | 539 +++++++++++ .../json/stream/JsonGeneratorFactory.java | 89 ++ .../java/javax/json/stream/JsonLocation.java | 61 ++ .../java/javax/json/stream/JsonParser.java | 487 ++++++++++ .../javax/json/stream/JsonParserFactory.java | 108 +++ .../json/stream/JsonParsingException.java | 72 ++ .../java/javax/json/stream/package-info.java | 47 + api/src/main/javadoc/overview.html | 108 +++ api/src/main/jdk9/module-info.java | 22 + bundles/licensee/pom.xml | 85 ++ .../licensee/src/main/assembly/archive.xml | 46 + bundles/pom.xml | 48 + bundles/ri/pom.xml | 87 ++ bundles/ri/src/main/assembly/archive.xml | 37 + bundles/ri/src/main/resources/LICENSE.md | 637 ++++++++++++ bundles/ri/src/main/resources/README.txt | 22 + copyright-exclude | 9 + demos/LICENSE.md | 29 + demos/customprovider-jdk9/pom.xml | 94 ++ .../json/customprovider/TestGenerator.java | 175 ++++ .../json/customprovider/TestProvider.java | 113 +++ .../src/main/jdk9/module-info.java | 15 + .../customprovider/test/TestProviderTest.java | 34 + demos/facebook/pom.xml | 151 +++ .../facebook/FacebookObjectSearch.java | 73 ++ .../facebook/FacebookStreamSearch.java | 64 ++ demos/facebook/src/main/jdk9/module-info.java | 13 + .../main/resources/facebookconfig.properties | 11 + demos/jaxrs/pom.xml | 59 ++ .../jsondemos/jaxrs/ArrayResource.java | 45 + .../jsondemos/jaxrs/DemoApplication.java | 47 + .../jsondemos/jaxrs/GeneratorResource.java | 67 ++ .../jsondemos/jaxrs/ObjectResource.java | 55 ++ .../jsondemos/jaxrs/ParserResource.java | 92 ++ .../jsondemos/jaxrs/StructureResource.java | 54 ++ demos/jsonpointer/pom.xml | 150 +++ .../jsonpointer/JsonpointerDemo.java | 131 +++ .../src/main/jdk9/module-info.java | 13 + .../src/main/resources/jsonpointer.json | 12 + .../jsonpointer/src/main/resources/wiki.json | 21 + demos/pom.xml | 69 ++ demos/servlet/pom.xml | 54 ++ .../jsondemos/servlet/ArrayServlet.java | 57 ++ demos/twitter/pom.xml | 154 +++ .../twitter/TwitterObjectSearch.java | 150 +++ .../twitter/TwitterStreamSearch.java | 80 ++ demos/twitter/src/main/jdk9/module-info.java | 14 + .../main/resources/twitterconfig.properties | 15 + gf/customprovider/pom.xml | 38 + .../json/customprovider/TestGenerator.java | 171 ++++ .../json/customprovider/TestProvider.java | 112 +++ .../json/customprovider/TestServlet.java | 50 + .../services/javax.json.spi.JsonProvider | 1 + gf/defaultprovider/pom.xml | 38 + .../json/defaultprovider/TestServlet.java | 46 + gf/pom.xml | 59 ++ impl/pom.xml | 149 +++ .../org/glassfish/json/BufferPoolImpl.java | 74 ++ .../glassfish/json/JsonArrayBuilderImpl.java | 479 +++++++++ .../json/JsonBuilderFactoryImpl.java | 76 ++ .../json/JsonGeneratorFactoryImpl.java | 70 ++ .../org/glassfish/json/JsonGeneratorImpl.java | 707 ++++++++++++++ .../org/glassfish/json/JsonLocationImpl.java | 57 ++ .../glassfish/json/JsonMergePatchImpl.java | 118 +++ .../java/org/glassfish/json/JsonMessages.java | 286 ++++++ .../org/glassfish/json/JsonNumberImpl.java | 264 +++++ .../glassfish/json/JsonObjectBuilderImpl.java | 327 +++++++ .../glassfish/json/JsonParserFactoryImpl.java | 71 ++ .../org/glassfish/json/JsonParserImpl.java | 539 +++++++++++ .../glassfish/json/JsonPatchBuilderImpl.java | 338 +++++++ .../org/glassfish/json/JsonPatchImpl.java | 307 ++++++ .../org/glassfish/json/JsonPointerImpl.java | 292 ++++++ .../json/JsonPrettyGeneratorImpl.java | 104 ++ .../org/glassfish/json/JsonProviderImpl.java | 272 ++++++ .../glassfish/json/JsonReaderFactoryImpl.java | 59 ++ .../org/glassfish/json/JsonReaderImpl.java | 135 +++ .../org/glassfish/json/JsonStringImpl.java | 108 +++ .../glassfish/json/JsonStructureParser.java | 311 ++++++ .../org/glassfish/json/JsonTokenizer.java | 586 +++++++++++ .../java/org/glassfish/json/JsonUtil.java | 75 ++ .../glassfish/json/JsonWriterFactoryImpl.java | 62 ++ .../org/glassfish/json/JsonWriterImpl.java | 167 ++++ .../main/java/org/glassfish/json/MapUtil.java | 91 ++ .../org/glassfish/json/NodeReference.java | 283 ++++++ .../json/UnicodeDetectingInputStream.java | 164 ++++ .../org/glassfish/json/api/BufferPool.java | 44 + impl/src/main/jdk9/module-info.java | 21 + .../org/glassfish/json/messages.properties | 88 ++ jaxrs-1x/pom.xml | 121 +++ .../json/jaxrs1x/JsonStructureBodyReader.java | 76 ++ .../json/jaxrs1x/JsonStructureBodyWriter.java | 91 ++ jaxrs/pom.xml | 120 +++ .../json/jaxrs/JsonValueBodyReader.java | 73 ++ .../json/jaxrs/JsonValueBodyWriter.java | 98 ++ pom.xml | 447 +++++++++ tests/pom.xml | 49 + .../glassfish/json/tests/JsonArrayTest.java | 114 +++ .../json/tests/JsonBuilderFactoryTest.java | 77 ++ .../glassfish/json/tests/JsonBuilderTest.java | 161 ++++ .../json/tests/JsonCollectorTest.java | 165 ++++ .../glassfish/json/tests/JsonFieldTest.java | 198 ++++ .../json/tests/JsonGeneratorFactoryTest.java | 70 ++ .../json/tests/JsonGeneratorTest.java | 519 ++++++++++ .../json/tests/JsonMergePatchDiffTest.java | 111 +++ .../json/tests/JsonMergePatchTest.java | 113 +++ .../glassfish/json/tests/JsonNumberTest.java | 210 ++++ .../glassfish/json/tests/JsonObjectTest.java | 125 +++ .../json/tests/JsonParserFactoryTest.java | 56 ++ .../json/tests/JsonParserSkipTest.java | 116 +++ .../glassfish/json/tests/JsonParserTest.java | 766 +++++++++++++++ .../json/tests/JsonParsingExceptionTest.java | 155 +++ .../json/tests/JsonPatchBugsTest.java | 45 + .../json/tests/JsonPatchBuilderTest.java | 83 ++ .../json/tests/JsonPatchDiffTest.java | 114 +++ .../json/tests/JsonPatchOperationTest.java | 46 + .../glassfish/json/tests/JsonPatchTest.java | 118 +++ .../tests/JsonPointerAddOperationTest.java | 203 ++++ .../json/tests/JsonPointerEscapeTest.java | 43 + .../tests/JsonPointerRemoveOperationTest.java | 157 +++ .../JsonPointerReplaceOperationTest.java | 201 ++++ .../glassfish/json/tests/JsonPointerTest.java | 108 +++ .../glassfish/json/tests/JsonReaderTest.java | 208 ++++ .../json/tests/JsonSamplesParsingTest.java | 65 ++ .../glassfish/json/tests/JsonStringTest.java | 57 ++ .../glassfish/json/tests/JsonValueTest.java | 171 ++++ .../glassfish/json/tests/JsonWriterTest.java | 229 +++++ .../org/glassfish/json/tests/RFC7159Test.java | 102 ++ .../org/glassfish/json/tests/ToJsonTest.java | 61 ++ .../json/tests/TwitterSearchTest.java | 75 ++ tests/src/test/resources/facebook.json | 668 +++++++++++++ tests/src/test/resources/facebook1.json | 911 ++++++++++++++++++ tests/src/test/resources/facebook2.json | 555 +++++++++++ tests/src/test/resources/jsonmergepatch.json | 103 ++ .../test/resources/jsonmergepatchdiff.json | 60 ++ tests/src/test/resources/jsonpatch.json | 219 +++++ tests/src/test/resources/jsonpatchdiff.json | 160 +++ tests/src/test/resources/rfc6901.json | 20 + tests/src/test/resources/twitter.json | 2 + tests/src/test/resources/wiki.json | 21 + 170 files changed, 26056 insertions(+), 203 deletions(-) delete mode 100644 LICENSE create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 api/pom.xml create mode 100644 api/src/main/java/javax/json/EmptyArray.java create mode 100644 api/src/main/java/javax/json/EmptyObject.java create mode 100644 api/src/main/java/javax/json/Json.java create mode 100644 api/src/main/java/javax/json/JsonArray.java create mode 100644 api/src/main/java/javax/json/JsonArrayBuilder.java create mode 100644 api/src/main/java/javax/json/JsonBuilderFactory.java create mode 100644 api/src/main/java/javax/json/JsonException.java create mode 100644 api/src/main/java/javax/json/JsonMergePatch.java create mode 100644 api/src/main/java/javax/json/JsonNumber.java create mode 100644 api/src/main/java/javax/json/JsonObject.java create mode 100644 api/src/main/java/javax/json/JsonObjectBuilder.java create mode 100644 api/src/main/java/javax/json/JsonPatch.java create mode 100644 api/src/main/java/javax/json/JsonPatchBuilder.java create mode 100644 api/src/main/java/javax/json/JsonPointer.java create mode 100644 api/src/main/java/javax/json/JsonReader.java create mode 100644 api/src/main/java/javax/json/JsonReaderFactory.java create mode 100644 api/src/main/java/javax/json/JsonString.java create mode 100644 api/src/main/java/javax/json/JsonStructure.java create mode 100644 api/src/main/java/javax/json/JsonValue.java create mode 100644 api/src/main/java/javax/json/JsonValueImpl.java create mode 100644 api/src/main/java/javax/json/JsonWriter.java create mode 100644 api/src/main/java/javax/json/JsonWriterFactory.java create mode 100644 api/src/main/java/javax/json/package-info.java create mode 100644 api/src/main/java/javax/json/spi/JsonProvider.java create mode 100644 api/src/main/java/javax/json/spi/package-info.java create mode 100644 api/src/main/java/javax/json/stream/JsonCollectors.java create mode 100644 api/src/main/java/javax/json/stream/JsonGenerationException.java create mode 100644 api/src/main/java/javax/json/stream/JsonGenerator.java create mode 100644 api/src/main/java/javax/json/stream/JsonGeneratorFactory.java create mode 100644 api/src/main/java/javax/json/stream/JsonLocation.java create mode 100644 api/src/main/java/javax/json/stream/JsonParser.java create mode 100644 api/src/main/java/javax/json/stream/JsonParserFactory.java create mode 100644 api/src/main/java/javax/json/stream/JsonParsingException.java create mode 100644 api/src/main/java/javax/json/stream/package-info.java create mode 100644 api/src/main/javadoc/overview.html create mode 100644 api/src/main/jdk9/module-info.java create mode 100755 bundles/licensee/pom.xml create mode 100755 bundles/licensee/src/main/assembly/archive.xml create mode 100644 bundles/pom.xml create mode 100755 bundles/ri/pom.xml create mode 100755 bundles/ri/src/main/assembly/archive.xml create mode 100644 bundles/ri/src/main/resources/LICENSE.md create mode 100644 bundles/ri/src/main/resources/README.txt create mode 100644 copyright-exclude create mode 100644 demos/LICENSE.md create mode 100644 demos/customprovider-jdk9/pom.xml create mode 100644 demos/customprovider-jdk9/src/main/java/org/glassfish/json/customprovider/TestGenerator.java create mode 100644 demos/customprovider-jdk9/src/main/java/org/glassfish/json/customprovider/TestProvider.java create mode 100644 demos/customprovider-jdk9/src/main/jdk9/module-info.java create mode 100644 demos/customprovider-jdk9/src/test/java/customprovider/test/TestProviderTest.java create mode 100644 demos/facebook/pom.xml create mode 100644 demos/facebook/src/main/java/org/glassfish/jsondemos/facebook/FacebookObjectSearch.java create mode 100644 demos/facebook/src/main/java/org/glassfish/jsondemos/facebook/FacebookStreamSearch.java create mode 100644 demos/facebook/src/main/jdk9/module-info.java create mode 100644 demos/facebook/src/main/resources/facebookconfig.properties create mode 100644 demos/jaxrs/pom.xml create mode 100644 demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/ArrayResource.java create mode 100644 demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/DemoApplication.java create mode 100644 demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/GeneratorResource.java create mode 100644 demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/ObjectResource.java create mode 100644 demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/ParserResource.java create mode 100644 demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/StructureResource.java create mode 100644 demos/jsonpointer/pom.xml create mode 100644 demos/jsonpointer/src/main/java/org/glassfish/jsondemos/jsonpointer/JsonpointerDemo.java create mode 100644 demos/jsonpointer/src/main/jdk9/module-info.java create mode 100644 demos/jsonpointer/src/main/resources/jsonpointer.json create mode 100644 demos/jsonpointer/src/main/resources/wiki.json create mode 100644 demos/pom.xml create mode 100644 demos/servlet/pom.xml create mode 100644 demos/servlet/src/main/java/org/glassfish/jsondemos/servlet/ArrayServlet.java create mode 100644 demos/twitter/pom.xml create mode 100644 demos/twitter/src/main/java/org/glassfish/jsondemos/twitter/TwitterObjectSearch.java create mode 100644 demos/twitter/src/main/java/org/glassfish/jsondemos/twitter/TwitterStreamSearch.java create mode 100644 demos/twitter/src/main/jdk9/module-info.java create mode 100644 demos/twitter/src/main/resources/twitterconfig.properties create mode 100644 gf/customprovider/pom.xml create mode 100644 gf/customprovider/src/main/java/org/glassfish/json/customprovider/TestGenerator.java create mode 100644 gf/customprovider/src/main/java/org/glassfish/json/customprovider/TestProvider.java create mode 100644 gf/customprovider/src/main/java/org/glassfish/json/customprovider/TestServlet.java create mode 100644 gf/customprovider/src/main/resources/META-INF/services/javax.json.spi.JsonProvider create mode 100644 gf/defaultprovider/pom.xml create mode 100644 gf/defaultprovider/src/main/java/org/glassfish/json/defaultprovider/TestServlet.java create mode 100644 gf/pom.xml create mode 100644 impl/pom.xml create mode 100644 impl/src/main/java/org/glassfish/json/BufferPoolImpl.java create mode 100644 impl/src/main/java/org/glassfish/json/JsonArrayBuilderImpl.java create mode 100644 impl/src/main/java/org/glassfish/json/JsonBuilderFactoryImpl.java create mode 100644 impl/src/main/java/org/glassfish/json/JsonGeneratorFactoryImpl.java create mode 100644 impl/src/main/java/org/glassfish/json/JsonGeneratorImpl.java create mode 100644 impl/src/main/java/org/glassfish/json/JsonLocationImpl.java create mode 100644 impl/src/main/java/org/glassfish/json/JsonMergePatchImpl.java create mode 100644 impl/src/main/java/org/glassfish/json/JsonMessages.java create mode 100644 impl/src/main/java/org/glassfish/json/JsonNumberImpl.java create mode 100644 impl/src/main/java/org/glassfish/json/JsonObjectBuilderImpl.java create mode 100644 impl/src/main/java/org/glassfish/json/JsonParserFactoryImpl.java create mode 100644 impl/src/main/java/org/glassfish/json/JsonParserImpl.java create mode 100644 impl/src/main/java/org/glassfish/json/JsonPatchBuilderImpl.java create mode 100644 impl/src/main/java/org/glassfish/json/JsonPatchImpl.java create mode 100644 impl/src/main/java/org/glassfish/json/JsonPointerImpl.java create mode 100644 impl/src/main/java/org/glassfish/json/JsonPrettyGeneratorImpl.java create mode 100644 impl/src/main/java/org/glassfish/json/JsonProviderImpl.java create mode 100644 impl/src/main/java/org/glassfish/json/JsonReaderFactoryImpl.java create mode 100644 impl/src/main/java/org/glassfish/json/JsonReaderImpl.java create mode 100644 impl/src/main/java/org/glassfish/json/JsonStringImpl.java create mode 100644 impl/src/main/java/org/glassfish/json/JsonStructureParser.java create mode 100644 impl/src/main/java/org/glassfish/json/JsonTokenizer.java create mode 100644 impl/src/main/java/org/glassfish/json/JsonUtil.java create mode 100644 impl/src/main/java/org/glassfish/json/JsonWriterFactoryImpl.java create mode 100644 impl/src/main/java/org/glassfish/json/JsonWriterImpl.java create mode 100644 impl/src/main/java/org/glassfish/json/MapUtil.java create mode 100644 impl/src/main/java/org/glassfish/json/NodeReference.java create mode 100644 impl/src/main/java/org/glassfish/json/UnicodeDetectingInputStream.java create mode 100644 impl/src/main/java/org/glassfish/json/api/BufferPool.java create mode 100644 impl/src/main/jdk9/module-info.java create mode 100644 impl/src/main/resources/org/glassfish/json/messages.properties create mode 100644 jaxrs-1x/pom.xml create mode 100644 jaxrs-1x/src/main/java/org/glassfish/json/jaxrs1x/JsonStructureBodyReader.java create mode 100644 jaxrs-1x/src/main/java/org/glassfish/json/jaxrs1x/JsonStructureBodyWriter.java create mode 100644 jaxrs/pom.xml create mode 100644 jaxrs/src/main/java/org/glassfish/json/jaxrs/JsonValueBodyReader.java create mode 100644 jaxrs/src/main/java/org/glassfish/json/jaxrs/JsonValueBodyWriter.java create mode 100644 pom.xml create mode 100644 tests/pom.xml create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonArrayTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonBuilderFactoryTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonBuilderTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonCollectorTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonFieldTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonGeneratorFactoryTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonGeneratorTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonMergePatchDiffTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonMergePatchTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonNumberTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonObjectTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonParserFactoryTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonParserSkipTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonParserTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonParsingExceptionTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonPatchBugsTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonPatchBuilderTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonPatchDiffTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonPatchOperationTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonPatchTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonPointerAddOperationTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonPointerEscapeTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonPointerRemoveOperationTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonPointerReplaceOperationTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonPointerTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonReaderTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonSamplesParsingTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonStringTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonValueTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/JsonWriterTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/RFC7159Test.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/ToJsonTest.java create mode 100644 tests/src/test/java/org/glassfish/json/tests/TwitterSearchTest.java create mode 100644 tests/src/test/resources/facebook.json create mode 100644 tests/src/test/resources/facebook1.json create mode 100644 tests/src/test/resources/facebook2.json create mode 100644 tests/src/test/resources/jsonmergepatch.json create mode 100644 tests/src/test/resources/jsonmergepatchdiff.json create mode 100644 tests/src/test/resources/jsonpatch.json create mode 100644 tests/src/test/resources/jsonpatchdiff.json create mode 100644 tests/src/test/resources/rfc6901.json create mode 100644 tests/src/test/resources/twitter.json create mode 100644 tests/src/test/resources/wiki.json diff --git a/LICENSE b/LICENSE deleted file mode 100644 index f735bee0..00000000 --- a/LICENSE +++ /dev/null @@ -1,203 +0,0 @@ -Eclipse Public License - v 1.0 - -THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC -LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM -CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. - -1. DEFINITIONS - -"Contribution" means: - -a) in the case of the initial Contributor, the initial code and documentation - distributed under this Agreement, and -b) in the case of each subsequent Contributor: - i) changes to the Program, and - ii) additions to the Program; - - where such changes and/or additions to the Program originate from and are - distributed by that particular Contributor. A Contribution 'originates' - from a Contributor if it was added to the Program by such Contributor - itself or anyone acting on such Contributor's behalf. Contributions do not - include additions to the Program which: (i) are separate modules of - software distributed in conjunction with the Program under their own - license agreement, and (ii) are not derivative works of the Program. - -"Contributor" means any person or entity that distributes the Program. - -"Licensed Patents" mean patent claims licensable by a Contributor which are -necessarily infringed by the use or sale of its Contribution alone or when -combined with the Program. - -"Program" means the Contributions distributed in accordance with this -Agreement. - -"Recipient" means anyone who receives the Program under this Agreement, -including all Contributors. - -2. GRANT OF RIGHTS - a) Subject to the terms of this Agreement, each Contributor hereby grants - Recipient a non-exclusive, worldwide, royalty-free copyright license to - reproduce, prepare derivative works of, publicly display, publicly - perform, distribute and sublicense the Contribution of such Contributor, - if any, and such derivative works, in source code and object code form. - b) Subject to the terms of this Agreement, each Contributor hereby grants - Recipient a non-exclusive, worldwide, royalty-free patent license under - Licensed Patents to make, use, sell, offer to sell, import and otherwise - transfer the Contribution of such Contributor, if any, in source code and - object code form. This patent license shall apply to the combination of - the Contribution and the Program if, at the time the Contribution is - added by the Contributor, such addition of the Contribution causes such - combination to be covered by the Licensed Patents. The patent license - shall not apply to any other combinations which include the Contribution. - No hardware per se is licensed hereunder. - c) Recipient understands that although each Contributor grants the licenses - to its Contributions set forth herein, no assurances are provided by any - Contributor that the Program does not infringe the patent or other - intellectual property rights of any other entity. Each Contributor - disclaims any liability to Recipient for claims brought by any other - entity based on infringement of intellectual property rights or - otherwise. As a condition to exercising the rights and licenses granted - hereunder, each Recipient hereby assumes sole responsibility to secure - any other intellectual property rights needed, if any. For example, if a - third party patent license is required to allow Recipient to distribute - the Program, it is Recipient's responsibility to acquire that license - before distributing the Program. - d) Each Contributor represents that to its knowledge it has sufficient - copyright rights in its Contribution, if any, to grant the copyright - license set forth in this Agreement. - -3. REQUIREMENTS - -A Contributor may choose to distribute the Program in object code form under -its own license agreement, provided that: - - a) it complies with the terms and conditions of this Agreement; and - b) its license agreement: - i) effectively disclaims on behalf of all Contributors all warranties - and conditions, express and implied, including warranties or - conditions of title and non-infringement, and implied warranties or - conditions of merchantability and fitness for a particular purpose; - ii) effectively excludes on behalf of all Contributors all liability for - damages, including direct, indirect, special, incidental and - consequential damages, such as lost profits; - iii) states that any provisions which differ from this Agreement are - offered by that Contributor alone and not by any other party; and - iv) states that source code for the Program is available from such - Contributor, and informs licensees how to obtain it in a reasonable - manner on or through a medium customarily used for software exchange. - -When the Program is made available in source code form: - - a) it must be made available under this Agreement; and - b) a copy of this Agreement must be included with each copy of the Program. - Contributors may not remove or alter any copyright notices contained - within the Program. - -Each Contributor must identify itself as the originator of its Contribution, -if -any, in a manner that reasonably allows subsequent Recipients to identify the -originator of the Contribution. - -4. COMMERCIAL DISTRIBUTION - -Commercial distributors of software may accept certain responsibilities with -respect to end users, business partners and the like. While this license is -intended to facilitate the commercial use of the Program, the Contributor who -includes the Program in a commercial product offering should do so in a manner -which does not create potential liability for other Contributors. Therefore, -if a Contributor includes the Program in a commercial product offering, such -Contributor ("Commercial Contributor") hereby agrees to defend and indemnify -every other Contributor ("Indemnified Contributor") against any losses, -damages and costs (collectively "Losses") arising from claims, lawsuits and -other legal actions brought by a third party against the Indemnified -Contributor to the extent caused by the acts or omissions of such Commercial -Contributor in connection with its distribution of the Program in a commercial -product offering. The obligations in this section do not apply to any claims -or Losses relating to any actual or alleged intellectual property -infringement. In order to qualify, an Indemnified Contributor must: -a) promptly notify the Commercial Contributor in writing of such claim, and -b) allow the Commercial Contributor to control, and cooperate with the -Commercial Contributor in, the defense and any related settlement -negotiations. The Indemnified Contributor may participate in any such claim at -its own expense. - -For example, a Contributor might include the Program in a commercial product -offering, Product X. That Contributor is then a Commercial Contributor. If -that Commercial Contributor then makes performance claims, or offers -warranties related to Product X, those performance claims and warranties are -such Commercial Contributor's responsibility alone. Under this section, the -Commercial Contributor would have to defend claims against the other -Contributors related to those performance claims and warranties, and if a -court requires any other Contributor to pay any damages as a result, the -Commercial Contributor must pay those damages. - -5. NO WARRANTY - -EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN -"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR -IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, -NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each -Recipient is solely responsible for determining the appropriateness of using -and distributing the Program and assumes all risks associated with its -exercise of rights under this Agreement , including but not limited to the -risks and costs of program errors, compliance with applicable laws, damage to -or loss of data, programs or equipment, and unavailability or interruption of -operations. - -6. DISCLAIMER OF LIABILITY - -EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY -CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION -LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE -EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY -OF SUCH DAMAGES. - -7. GENERAL - -If any provision of this Agreement is invalid or unenforceable under -applicable law, it shall not affect the validity or enforceability of the -remainder of the terms of this Agreement, and without further action by the -parties hereto, such provision shall be reformed to the minimum extent -necessary to make such provision valid and enforceable. - -If Recipient institutes patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Program itself -(excluding combinations of the Program with other software or hardware) -infringes such Recipient's patent(s), then such Recipient's rights granted -under Section 2(b) shall terminate as of the date such litigation is filed. - -All Recipient's rights under this Agreement shall terminate if it fails to -comply with any of the material terms or conditions of this Agreement and does -not cure such failure in a reasonable period of time after becoming aware of -such noncompliance. If all Recipient's rights under this Agreement terminate, -Recipient agrees to cease use and distribution of the Program as soon as -reasonably practicable. However, Recipient's obligations under this Agreement -and any licenses granted by Recipient relating to the Program shall continue -and survive. - -Everyone is permitted to copy and distribute copies of this Agreement, but in -order to avoid inconsistency the Agreement is copyrighted and may only be -modified in the following manner. The Agreement Steward reserves the right to -publish new versions (including revisions) of this Agreement from time to -time. No one other than the Agreement Steward has the right to modify this -Agreement. The Eclipse Foundation is the initial Agreement Steward. The -Eclipse Foundation may assign the responsibility to serve as the Agreement -Steward to a suitable separate entity. Each new version of the Agreement will -be given a distinguishing version number. The Program (including -Contributions) may always be distributed subject to the version of the -Agreement under which it was received. In addition, after a new version of the -Agreement is published, Contributor may elect to distribute the Program -(including its Contributions) under the new version. Except as expressly -stated in Sections 2(a) and 2(b) above, Recipient receives no rights or -licenses to the intellectual property of any Contributor under this Agreement, -whether expressly, by implication, estoppel or otherwise. All rights in the -Program not expressly granted under this Agreement are reserved. - -This Agreement is governed by the laws of the State of New York and the -intellectual property laws of the United States of America. No party to this -Agreement will bring a legal action under this Agreement more than one year -after the cause of action arose. Each party waives its rights to a jury trial in -any resulting litigation. diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..5de3d1b4 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,637 @@ +# Eclipse Public License - v 2.0 + + THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE + PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION + OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + + 1. DEFINITIONS + + "Contribution" means: + + a) in the case of the initial Contributor, the initial content + Distributed under this Agreement, and + + b) in the case of each subsequent Contributor: + i) changes to the Program, and + ii) additions to the Program; + where such changes and/or additions to the Program originate from + and are Distributed by that particular Contributor. A Contribution + "originates" from a Contributor if it was added to the Program by + such Contributor itself or anyone acting on such Contributor's behalf. + Contributions do not include changes or additions to the Program that + are not Modified Works. + + "Contributor" means any person or entity that Distributes the Program. + + "Licensed Patents" mean patent claims licensable by a Contributor which + are necessarily infringed by the use or sale of its Contribution alone + or when combined with the Program. + + "Program" means the Contributions Distributed in accordance with this + Agreement. + + "Recipient" means anyone who receives the Program under this Agreement + or any Secondary License (as applicable), including Contributors. + + "Derivative Works" shall mean any work, whether in Source Code or other + form, that is based on (or derived from) the Program and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. + + "Modified Works" shall mean any work in Source Code or other form that + results from an addition to, deletion from, or modification of the + contents of the Program, including, for purposes of clarity any new file + in Source Code form that contains any contents of the Program. Modified + Works shall not include works that contain only declarations, + interfaces, types, classes, structures, or files of the Program solely + in each case in order to link to, bind by name, or subclass the Program + or Modified Works thereof. + + "Distribute" means the acts of a) distributing or b) making available + in any manner that enables the transfer of a copy. + + "Source Code" means the form of a Program preferred for making + modifications, including but not limited to software source code, + documentation source, and configuration files. + + "Secondary License" means either the GNU General Public License, + Version 2.0, or any later versions of that license, including any + exceptions or additional permissions as identified by the initial + Contributor. + + 2. GRANT OF RIGHTS + + a) Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free copyright + license to reproduce, prepare Derivative Works of, publicly display, + publicly perform, Distribute and sublicense the Contribution of such + Contributor, if any, and such Derivative Works. + + b) Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free patent + license under Licensed Patents to make, use, sell, offer to sell, + import and otherwise transfer the Contribution of such Contributor, + if any, in Source Code or other form. This patent license shall + apply to the combination of the Contribution and the Program if, at + the time the Contribution is added by the Contributor, such addition + of the Contribution causes such combination to be covered by the + Licensed Patents. The patent license shall not apply to any other + combinations which include the Contribution. No hardware per se is + licensed hereunder. + + c) Recipient understands that although each Contributor grants the + licenses to its Contributions set forth herein, no assurances are + provided by any Contributor that the Program does not infringe the + patent or other intellectual property rights of any other entity. + Each Contributor disclaims any liability to Recipient for claims + brought by any other entity based on infringement of intellectual + property rights or otherwise. As a condition to exercising the + rights and licenses granted hereunder, each Recipient hereby + assumes sole responsibility to secure any other intellectual + property rights needed, if any. For example, if a third party + patent license is required to allow Recipient to Distribute the + Program, it is Recipient's responsibility to acquire that license + before distributing the Program. + + d) Each Contributor represents that to its knowledge it has + sufficient copyright rights in its Contribution, if any, to grant + the copyright license set forth in this Agreement. + + e) Notwithstanding the terms of any Secondary License, no + Contributor makes additional grants to any Recipient (other than + those set forth in this Agreement) as a result of such Recipient's + receipt of the Program under the terms of a Secondary License + (if permitted under the terms of Section 3). + + 3. REQUIREMENTS + + 3.1 If a Contributor Distributes the Program in any form, then: + + a) the Program must also be made available as Source Code, in + accordance with section 3.2, and the Contributor must accompany + the Program with a statement that the Source Code for the Program + is available under this Agreement, and informs Recipients how to + obtain it in a reasonable manner on or through a medium customarily + used for software exchange; and + + b) the Contributor may Distribute the Program under a license + different than this Agreement, provided that such license: + i) effectively disclaims on behalf of all other Contributors all + warranties and conditions, express and implied, including + warranties or conditions of title and non-infringement, and + implied warranties or conditions of merchantability and fitness + for a particular purpose; + + ii) effectively excludes on behalf of all other Contributors all + liability for damages, including direct, indirect, special, + incidental and consequential damages, such as lost profits; + + iii) does not attempt to limit or alter the recipients' rights + in the Source Code under section 3.2; and + + iv) requires any subsequent distribution of the Program by any + party to be under a license that satisfies the requirements + of this section 3. + + 3.2 When the Program is Distributed as Source Code: + + a) it must be made available under this Agreement, or if the + Program (i) is combined with other material in a separate file or + files made available under a Secondary License, and (ii) the initial + Contributor attached to the Source Code the notice described in + Exhibit A of this Agreement, then the Program may be made available + under the terms of such Secondary Licenses, and + + b) a copy of this Agreement must be included with each copy of + the Program. + + 3.3 Contributors may not remove or alter any copyright, patent, + trademark, attribution notices, disclaimers of warranty, or limitations + of liability ("notices") contained within the Program from any copy of + the Program which they Distribute, provided that Contributors may add + their own appropriate notices. + + 4. COMMERCIAL DISTRIBUTION + + Commercial distributors of software may accept certain responsibilities + with respect to end users, business partners and the like. While this + license is intended to facilitate the commercial use of the Program, + the Contributor who includes the Program in a commercial product + offering should do so in a manner which does not create potential + liability for other Contributors. Therefore, if a Contributor includes + the Program in a commercial product offering, such Contributor + ("Commercial Contributor") hereby agrees to defend and indemnify every + other Contributor ("Indemnified Contributor") against any losses, + damages and costs (collectively "Losses") arising from claims, lawsuits + and other legal actions brought by a third party against the Indemnified + Contributor to the extent caused by the acts or omissions of such + Commercial Contributor in connection with its distribution of the Program + in a commercial product offering. The obligations in this section do not + apply to any claims or Losses relating to any actual or alleged + intellectual property infringement. In order to qualify, an Indemnified + Contributor must: a) promptly notify the Commercial Contributor in + writing of such claim, and b) allow the Commercial Contributor to control, + and cooperate with the Commercial Contributor in, the defense and any + related settlement negotiations. The Indemnified Contributor may + participate in any such claim at its own expense. + + For example, a Contributor might include the Program in a commercial + product offering, Product X. That Contributor is then a Commercial + Contributor. If that Commercial Contributor then makes performance + claims, or offers warranties related to Product X, those performance + claims and warranties are such Commercial Contributor's responsibility + alone. Under this section, the Commercial Contributor would have to + defend claims against the other Contributors related to those performance + claims and warranties, and if a court requires any other Contributor to + pay any damages as a result, the Commercial Contributor must pay + those damages. + + 5. NO WARRANTY + + EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT + PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS" + BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR + IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF + TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR + PURPOSE. Each Recipient is solely responsible for determining the + appropriateness of using and distributing the Program and assumes all + risks associated with its exercise of rights under this Agreement, + including but not limited to the risks and costs of program errors, + compliance with applicable laws, damage to or loss of data, programs + or equipment, and unavailability or interruption of operations. + + 6. DISCLAIMER OF LIABILITY + + EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT + PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS + SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST + PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE + EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGES. + + 7. GENERAL + + If any provision of this Agreement is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability of + the remainder of the terms of this Agreement, and without further + action by the parties hereto, such provision shall be reformed to the + minimum extent necessary to make such provision valid and enforceable. + + If Recipient institutes patent litigation against any entity + (including a cross-claim or counterclaim in a lawsuit) alleging that the + Program itself (excluding combinations of the Program with other software + or hardware) infringes such Recipient's patent(s), then such Recipient's + rights granted under Section 2(b) shall terminate as of the date such + litigation is filed. + + All Recipient's rights under this Agreement shall terminate if it + fails to comply with any of the material terms or conditions of this + Agreement and does not cure such failure in a reasonable period of + time after becoming aware of such noncompliance. If all Recipient's + rights under this Agreement terminate, Recipient agrees to cease use + and distribution of the Program as soon as reasonably practicable. + However, Recipient's obligations under this Agreement and any licenses + granted by Recipient relating to the Program shall continue and survive. + + Everyone is permitted to copy and distribute copies of this Agreement, + but in order to avoid inconsistency the Agreement is copyrighted and + may only be modified in the following manner. The Agreement Steward + reserves the right to publish new versions (including revisions) of + this Agreement from time to time. No one other than the Agreement + Steward has the right to modify this Agreement. The Eclipse Foundation + is the initial Agreement Steward. The Eclipse Foundation may assign the + responsibility to serve as the Agreement Steward to a suitable separate + entity. Each new version of the Agreement will be given a distinguishing + version number. The Program (including Contributions) may always be + Distributed subject to the version of the Agreement under which it was + received. In addition, after a new version of the Agreement is published, + Contributor may elect to Distribute the Program (including its + Contributions) under the new version. + + Except as expressly stated in Sections 2(a) and 2(b) above, Recipient + receives no rights or licenses to the intellectual property of any + Contributor under this Agreement, whether expressly, by implication, + estoppel or otherwise. All rights in the Program not expressly granted + under this Agreement are reserved. Nothing in this Agreement is intended + to be enforceable by any entity that is not a Contributor or Recipient. + No third-party beneficiary rights are created under this Agreement. + + Exhibit A - Form of Secondary Licenses Notice + + "This Source Code may also be made available under the following + Secondary Licenses when the conditions for such availability set forth + in the Eclipse Public License, v. 2.0 are satisfied: {name license(s), + version(s), and exceptions or additional permissions here}." + + Simply including a copy of this Agreement, including this Exhibit A + is not sufficient to license the Source Code under Secondary Licenses. + + If it is not possible or desirable to put the notice in a particular + file, then You may include the notice in a location (such as a LICENSE + file in a relevant directory) where a recipient would be likely to + look for such a notice. + + You may add additional accurate notices of copyright ownership. + +--- + +## The GNU General Public License (GPL) Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1335 + USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your freedom to + share and change it. By contrast, the GNU General Public License is + intended to guarantee your freedom to share and change free software--to + make sure the software is free for all its users. This General Public + License applies to most of the Free Software Foundation's software and + to any other program whose authors commit to using it. (Some other Free + Software Foundation software is covered by the GNU Library General + Public License instead.) You can apply it to your programs, too. + + When we speak of free software, we are referring to freedom, not price. + Our General Public Licenses are designed to make sure that you have the + freedom to distribute copies of free software (and charge for this + service if you wish), that you receive source code or can get it if you + want it, that you can change the software or use pieces of it in new + free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid anyone + to deny you these rights or to ask you to surrender the rights. These + restrictions translate to certain responsibilities for you if you + distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether gratis + or for a fee, you must give the recipients all the rights that you have. + You must make sure that they, too, receive or can get the source code. + And you must show them these terms so they know their rights. + + We protect your rights with two steps: (1) copyright the software, and + (2) offer you this license which gives you legal permission to copy, + distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain + that everyone understands that there is no warranty for this free + software. If the software is modified by someone else and passed on, we + want its recipients to know that what they have is not the original, so + that any problems introduced by others will not reflect on the original + authors' reputations. + + Finally, any free program is threatened constantly by software patents. + We wish to avoid the danger that redistributors of a free program will + individually obtain patent licenses, in effect making the program + proprietary. To prevent this, we have made it clear that any patent must + be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and + modification follow. + + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains a + notice placed by the copyright holder saying it may be distributed under + the terms of this General Public License. The "Program", below, refers + to any such program or work, and a "work based on the Program" means + either the Program or any derivative work under copyright law: that is + to say, a work containing the Program or a portion of it, either + verbatim or with modifications and/or translated into another language. + (Hereinafter, translation is included without limitation in the term + "modification".) Each licensee is addressed as "you". + + Activities other than copying, distribution and modification are not + covered by this License; they are outside its scope. The act of running + the Program is not restricted, and the output from the Program is + covered only if its contents constitute a work based on the Program + (independent of having been made by running the Program). Whether that + is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's source + code as you receive it, in any medium, provided that you conspicuously + and appropriately publish on each copy an appropriate copyright notice + and disclaimer of warranty; keep intact all the notices that refer to + this License and to the absence of any warranty; and give any other + recipients of the Program a copy of this License along with the Program. + + You may charge a fee for the physical act of transferring a copy, and + you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion of + it, thus forming a work based on the Program, and copy and distribute + such modifications or work under the terms of Section 1 above, provided + that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any part + thereof, to be licensed as a whole at no charge to all third parties + under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a notice + that there is no warranty (or else, saying that you provide a + warranty) and that users may redistribute the program under these + conditions, and telling the user how to view a copy of this License. + (Exception: if the Program itself is interactive but does not + normally print such an announcement, your work based on the Program + is not required to print an announcement.) + + These requirements apply to the modified work as a whole. If + identifiable sections of that work are not derived from the Program, and + can be reasonably considered independent and separate works in + themselves, then this License, and its terms, do not apply to those + sections when you distribute them as separate works. But when you + distribute the same sections as part of a whole which is a work based on + the Program, the distribution of the whole must be on the terms of this + License, whose permissions for other licensees extend to the entire + whole, and thus to each and every part regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or contest + your rights to work written entirely by you; rather, the intent is to + exercise the right to control the distribution of derivative or + collective works based on the Program. + + In addition, mere aggregation of another work not based on the Program + with the Program (or with a work based on the Program) on a volume of a + storage or distribution medium does not bring the other work under the + scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, + under Section 2) in object code or executable form under the terms of + Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections 1 + and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your cost + of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed + only for noncommercial distribution and only if you received the + program in object code or executable form with such an offer, in + accord with Subsection b above.) + + The source code for a work means the preferred form of the work for + making modifications to it. For an executable work, complete source code + means all the source code for all modules it contains, plus any + associated interface definition files, plus the scripts used to control + compilation and installation of the executable. However, as a special + exception, the source code distributed need not include anything that is + normally distributed (in either source or binary form) with the major + components (compiler, kernel, and so on) of the operating system on + which the executable runs, unless that component itself accompanies the + executable. + + If distribution of executable or object code is made by offering access + to copy from a designated place, then offering equivalent access to copy + the source code from the same place counts as distribution of the source + code, even though third parties are not compelled to copy the source + along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program + except as expressly provided under this License. Any attempt otherwise + to copy, modify, sublicense or distribute the Program is void, and will + automatically terminate your rights under this License. However, parties + who have received copies, or rights, from you under this License will + not have their licenses terminated so long as such parties remain in + full compliance. + + 5. You are not required to accept this License, since you have not + signed it. However, nothing else grants you permission to modify or + distribute the Program or its derivative works. These actions are + prohibited by law if you do not accept this License. Therefore, by + modifying or distributing the Program (or any work based on the + Program), you indicate your acceptance of this License to do so, and all + its terms and conditions for copying, distributing or modifying the + Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the + Program), the recipient automatically receives a license from the + original licensor to copy, distribute or modify the Program subject to + these terms and conditions. You may not impose any further restrictions + on the recipients' exercise of the rights granted herein. You are not + responsible for enforcing compliance by third parties to this License. + + 7. If, as a consequence of a court judgment or allegation of patent + infringement or for any other reason (not limited to patent issues), + conditions are imposed on you (whether by court order, agreement or + otherwise) that contradict the conditions of this License, they do not + excuse you from the conditions of this License. If you cannot distribute + so as to satisfy simultaneously your obligations under this License and + any other pertinent obligations, then as a consequence you may not + distribute the Program at all. For example, if a patent license would + not permit royalty-free redistribution of the Program by all those who + receive copies directly or indirectly through you, then the only way you + could satisfy both it and this License would be to refrain entirely from + distribution of the Program. + + If any portion of this section is held invalid or unenforceable under + any particular circumstance, the balance of the section is intended to + apply and the section as a whole is intended to apply in other + circumstances. + + It is not the purpose of this section to induce you to infringe any + patents or other property right claims or to contest validity of any + such claims; this section has the sole purpose of protecting the + integrity of the free software distribution system, which is implemented + by public license practices. Many people have made generous + contributions to the wide range of software distributed through that + system in reliance on consistent application of that system; it is up to + the author/donor to decide if he or she is willing to distribute + software through any other system and a licensee cannot impose that choice. + + This section is intended to make thoroughly clear what is believed to be + a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in + certain countries either by patents or by copyrighted interfaces, the + original copyright holder who places the Program under this License may + add an explicit geographical distribution limitation excluding those + countries, so that distribution is permitted only in or among countries + not thus excluded. In such case, this License incorporates the + limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new + versions of the General Public License from time to time. Such new + versions will be similar in spirit to the present version, but may + differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the Program + specifies a version number of this License which applies to it and "any + later version", you have the option of following the terms and + conditions either of that version or of any later version published by + the Free Software Foundation. If the Program does not specify a version + number of this License, you may choose any version ever published by the + Free Software Foundation. + + 10. If you wish to incorporate parts of the Program into other free + programs whose distribution conditions are different, write to the + author to ask for permission. For software which is copyrighted by the + Free Software Foundation, write to the Free Software Foundation; we + sometimes make exceptions for this. Our decision will be guided by the + two goals of preserving the free status of all derivatives of our free + software and of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO + WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. + EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR + OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, + EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE + ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH + YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL + NECESSARY SERVICING, REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN + WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY + AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR + DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL + DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM + (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED + INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF + THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR + OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest + possible use to the public, the best way to achieve this is to make it + free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest to + attach them to the start of each source file to most effectively convey + the exclusion of warranty; and each file should have at least the + "copyright" line and a pointer to where the full notice is found. + + One line to give the program's name and a brief idea of what it does. + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA + + Also add information on how to contact you by electronic and paper mail. + + If the program is interactive, make it output a short notice like this + when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type + `show w'. This is free software, and you are welcome to redistribute + it under certain conditions; type `show c' for details. + + The hypothetical commands `show w' and `show c' should show the + appropriate parts of the General Public License. Of course, the commands + you use may be called something other than `show w' and `show c'; they + could even be mouse-clicks or menu items--whatever suits your program. + + You should also get your employer (if you work as a programmer) or your + school, if any, to sign a "copyright disclaimer" for the program, if + necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + program `Gnomovision' (which makes passes at compilers) written by + James Hacker. + + signature of Ty Coon, 1 April 1989 + Ty Coon, President of Vice + + This General Public License does not permit incorporating your program + into proprietary programs. If your program is a subroutine library, you + may consider it more useful to permit linking proprietary applications + with the library. If this is what you want to do, use the GNU Library + General Public License instead of this License. + +--- + +## CLASSPATH EXCEPTION + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License version 2 cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from or + based on this library. If you modify this library, you may extend this + exception to your version of the library, but you are not obligated to + do so. If you do not wish to do so, delete this exception statement + from your version. diff --git a/README.md b/README.md new file mode 100644 index 00000000..a38c1f4c --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# JSON Processing (JSON-P) + +This projects contains API and Reference Implementation (RI) of JSR-374. +The Java API for JSON Processing provides portable APIs to parse, generate, transform, and query JSON documents. + +## Build + +Use the following command: +```bash +mvn -U -C clean install -Dnon.final=true +``` + +## Licensing + +- [Eclipse Public License 2.0](https://projects.eclipse.org/license/epl-2.0) +- [GNU General Public License, version 2 with the GNU Classpath Exception](https://projects.eclipse.org/license/secondary-gpl-2.0-cp) + +## Links + +- JSON-P official web site: https://javaee.github.io/jsonp +- JSR-374 page on JCP site: https://jcp.org/en/jsr/detail?id=374 \ No newline at end of file diff --git a/api/pom.xml b/api/pom.xml new file mode 100644 index 00000000..704f2254 --- /dev/null +++ b/api/pom.xml @@ -0,0 +1,128 @@ + + + + + 4.0.0 + + + org.glassfish + json + 1.2-SNAPSHOT + ../pom.xml + + + javax.json + javax.json-api + bundle + 1.2-SNAPSHOT + JSR 374 (JSON Processing) API + API module of JSR 374:Java API for Processing JSON + https://javaee.github.io/jsonp + + + javax.json.* + + + + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + true + + + JSON Processing Packages + javax.json* + + + +Oracle + and/or its affiliates. All Rights Reserved. + Use is subject to + license terms. +
Comments to : jsonp-spec@javaee.groups.io +]]> +
+ + http://docs.oracle.com/javase/8/docs/api/ + +
+ + + + attach-javadocs + + jar + + + +
+ + org.glassfish.build + spec-version-maven-plugin + + + ${non.final} + api + ${spec_version} + ${new_spec_impl_version} + ${new_spec_version} + ${api_package} + + + + + + set-spec-properties + check-module + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + ${spec.bundle.version} + ${spec.bundle.symbolic-name} + ${spec.extension.name} + ${spec.implementation.version} + ${spec.specification.version} + ${packages.export} + Java API for JSON Processing (JSON-P) ${spec_version} + + + +
+
+
diff --git a/api/src/main/java/javax/json/EmptyArray.java b/api/src/main/java/javax/json/EmptyArray.java new file mode 100644 index 00000000..405aa23e --- /dev/null +++ b/api/src/main/java/javax/json/EmptyArray.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json; + +import java.io.Serializable; +import java.util.AbstractList; +import java.util.Collections; +import java.util.List; +import java.util.RandomAccess; + +/** + * Private implementation of immutable {@link JsonArray}. + * + * @author Lukas Jungmann + */ +final class EmptyArray extends AbstractList implements JsonArray, Serializable, RandomAccess { + + private static final long serialVersionUID = 7295439472061642859L; + + @Override + public JsonValue get(int index) { + throw new IndexOutOfBoundsException("Index: " + index); + } + + @Override + public int size() { + return 0; + } + + @Override + public JsonObject getJsonObject(int index) { + return (JsonObject) get(index); + } + + @Override + public JsonArray getJsonArray(int index) { + return (JsonArray) get(index); + } + + @Override + public JsonNumber getJsonNumber(int index) { + return (JsonNumber) get(index); + } + + @Override + public JsonString getJsonString(int index) { + return (JsonString) get(index); + } + + @Override + public List getValuesAs(Class clazz) { + return Collections.emptyList(); + } + + @Override + public String getString(int index) { + return getJsonString(index).getString(); + } + + @Override + public String getString(int index, String defaultValue) { + return defaultValue; + } + + @Override + public int getInt(int index) { + return getJsonNumber(index).intValue(); + } + + @Override + public int getInt(int index, int defaultValue) { + return defaultValue; + } + + @Override + public boolean getBoolean(int index) { + return get(index) == JsonValue.TRUE; + } + + @Override + public boolean getBoolean(int index, boolean defaultValue) { + return defaultValue; + } + + @Override + public boolean isNull(int index) { + return get(index) == JsonValue.NULL; + } + + @Override + public ValueType getValueType() { + return ValueType.ARRAY; + } + + // Preserves singleton property + private Object readResolve() { + return JsonValue.EMPTY_JSON_ARRAY; + } +} diff --git a/api/src/main/java/javax/json/EmptyObject.java b/api/src/main/java/javax/json/EmptyObject.java new file mode 100644 index 00000000..9e5750af --- /dev/null +++ b/api/src/main/java/javax/json/EmptyObject.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json; + +import java.io.Serializable; +import java.util.AbstractMap; +import java.util.Collections; +import java.util.Set; + +/** + * Private implementation of immutable {@link JsonObject}. + * + * @author Lukas Jungmann + */ +final class EmptyObject extends AbstractMap implements JsonObject, Serializable { + + private static final long serialVersionUID = -1461653546889072583L; + + @Override + public Set> entrySet() { + return Collections.>emptySet(); + } + + @Override + public JsonArray getJsonArray(String name) { + return (JsonArray) get(name); + } + + @Override + public JsonObject getJsonObject(String name) { + return (JsonObject) get(name); + } + + @Override + public JsonNumber getJsonNumber(String name) { + return (JsonNumber) get(name); + } + + @Override + public JsonString getJsonString(String name) { + return (JsonString) get(name); + } + + @Override + public String getString(String name) { + return getJsonString(name).getString(); + } + + @Override + public String getString(String name, String defaultValue) { + return defaultValue; + } + + @Override + public int getInt(String name) { + return getJsonNumber(name).intValue(); + } + + @Override + public int getInt(String name, int defaultValue) { + return defaultValue; + } + + @Override + public boolean getBoolean(String name) { + throw new NullPointerException(); + } + + @Override + public boolean getBoolean(String name, boolean defaultValue) { + return defaultValue; + } + + @Override + public boolean isNull(String name) { + throw new NullPointerException(); + } + + @Override + public ValueType getValueType() { + return ValueType.OBJECT; + } + + // Preserves singleton property + private Object readResolve() { + return JsonValue.EMPTY_JSON_OBJECT; + } +} diff --git a/api/src/main/java/javax/json/Json.java b/api/src/main/java/javax/json/Json.java new file mode 100644 index 00000000..b6eb79ea --- /dev/null +++ b/api/src/main/java/javax/json/Json.java @@ -0,0 +1,520 @@ +/* + * Copyright (c) 2011, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import javax.json.spi.JsonProvider; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonGeneratorFactory; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParserFactory; + +/** + * Factory class for creating JSON processing objects. + * This class provides the most commonly used methods for creating these + * objects and their corresponding factories. The factory classes provide + * all the various ways to create these objects. + * + *

+ * The methods in this class locate a provider instance using the method + * {@link JsonProvider#provider()}. This class uses the provider instance + * to create JSON processing objects. + * + *

+ * The following example shows how to create a JSON parser to parse + * an empty array: + *

+ * 
+ * StringReader reader = new StringReader("[]");
+ * JsonParser parser = Json.createParser(reader);
+ * 
+ * 
+ * + *

+ * All the methods in this class are safe for use by multiple concurrent + * threads. + */ +public final class Json { + + private Json() { + } + + /** + * Creates a JSON parser from a character stream. + * + * @param reader i/o reader from which JSON is to be read + * @return a JSON parser + */ + public static JsonParser createParser(Reader reader) { + return JsonProvider.provider().createParser(reader); + } + + /** + * Creates a JSON parser from a byte stream. + * The character encoding of the stream is determined as specified in + * RFC 7159. + * + * @param in i/o stream from which JSON is to be read + * @throws JsonException if encoding cannot be determined + * or i/o error (IOException would be cause of JsonException) + * @return a JSON parser + */ + public static JsonParser createParser(InputStream in) { + return JsonProvider.provider().createParser(in); + } + + /** + * Creates a JSON generator for writing JSON to a character stream. + * + * @param writer a i/o writer to which JSON is written + * @return a JSON generator + */ + public static JsonGenerator createGenerator(Writer writer) { + return JsonProvider.provider().createGenerator(writer); + } + + /** + * Creates a JSON generator for writing JSON to a byte stream. + * + * @param out i/o stream to which JSON is written + * @return a JSON generator + */ + public static JsonGenerator createGenerator(OutputStream out) { + return JsonProvider.provider().createGenerator(out); + } + + /** + * Creates a parser factory for creating {@link JsonParser} objects. + * + * @return JSON parser factory. + * + public static JsonParserFactory createParserFactory() { + return JsonProvider.provider().createParserFactory(); + } + */ + + /** + * Creates a parser factory for creating {@link JsonParser} objects. + * The factory is configured with the specified map of provider specific + * configuration properties. Provider implementations should ignore any + * unsupported configuration properties specified in the map. + * + * @param config a map of provider specific properties to configure the + * JSON parsers. The map may be empty or null + * @return JSON parser factory + */ + public static JsonParserFactory createParserFactory(Map config) { + return JsonProvider.provider().createParserFactory(config); + } + + /** + * Creates a generator factory for creating {@link JsonGenerator} objects. + * + * @return JSON generator factory + * + public static JsonGeneratorFactory createGeneratorFactory() { + return JsonProvider.provider().createGeneratorFactory(); + } + */ + + /** + * Creates a generator factory for creating {@link JsonGenerator} objects. + * The factory is configured with the specified map of provider specific + * configuration properties. Provider implementations should ignore any + * unsupported configuration properties specified in the map. + * + * @param config a map of provider specific properties to configure the + * JSON generators. The map may be empty or null + * @return JSON generator factory + */ + public static JsonGeneratorFactory createGeneratorFactory( + Map config) { + return JsonProvider.provider().createGeneratorFactory(config); + } + + /** + * Creates a JSON writer to write a + * JSON {@link JsonObject object} or {@link JsonArray array} + * structure to the specified character stream. + * + * @param writer to which JSON object or array is written + * @return a JSON writer + */ + public static JsonWriter createWriter(Writer writer) { + return JsonProvider.provider().createWriter(writer); + } + + /** + * Creates a JSON writer to write a + * JSON {@link JsonObject object} or {@link JsonArray array} + * structure to the specified byte stream. Characters written to + * the stream are encoded into bytes using UTF-8 encoding. + * + * @param out to which JSON object or array is written + * @return a JSON writer + */ + public static JsonWriter createWriter(OutputStream out) { + return JsonProvider.provider().createWriter(out); + } + + /** + * Creates a JSON reader from a character stream. + * + * @param reader a reader from which JSON is to be read + * @return a JSON reader + */ + public static JsonReader createReader(Reader reader) { + return JsonProvider.provider().createReader(reader); + } + + /** + * Creates a JSON reader from a byte stream. The character encoding of + * the stream is determined as described in + * RFC 7159. + * + * @param in a byte stream from which JSON is to be read + * @return a JSON reader + */ + public static JsonReader createReader(InputStream in) { + return JsonProvider.provider().createReader(in); + } + + /** + * Creates a reader factory for creating {@link JsonReader} objects. + * The factory is configured with the specified map of provider specific + * configuration properties. Provider implementations should ignore any + * unsupported configuration properties specified in the map. + * + * @param config a map of provider specific properties to configure the + * JSON readers. The map may be empty or null + * @return a JSON reader factory + */ + public static JsonReaderFactory createReaderFactory(Map config) { + return JsonProvider.provider().createReaderFactory(config); + } + + /** + * Creates a writer factory for creating {@link JsonWriter} objects. + * The factory is configured with the specified map of provider specific + * configuration properties. Provider implementations should ignore any + * unsupported configuration properties specified in the map. + * + * @param config a map of provider specific properties to configure the + * JSON writers. The map may be empty or null + * @return a JSON writer factory + */ + public static JsonWriterFactory createWriterFactory(Map config) { + return JsonProvider.provider().createWriterFactory(config); + } + + /** + * Creates a JSON array builder + * + * @return a JSON array builder + */ + public static JsonArrayBuilder createArrayBuilder() { + return JsonProvider.provider().createArrayBuilder(); + } + + /** + * Creates a JSON array builder, initialized with the specified array + * + * @param array the initial array in the builder + * @return a JSON array builder + * + * @since 1.1 + */ + public static JsonArrayBuilder createArrayBuilder(JsonArray array) { + return JsonProvider.provider().createArrayBuilder(array); + } + + /** + * Creates a JSON array builder, initialized with the content of specified {@code collection}. + * If the @{code collection} contains {@link Optional}s then resulting JSON array builder + * contains the value from the {@code collection} only if the {@link Optional} is not empty. + * + * @param collection the initial data for the builder + * @return a JSON array builder + * @exception IllegalArgumentException if the value from the {@code collection} cannot be converted + * to the corresponding {@link JsonValue} + * + * @since 1.1 + */ + public static JsonArrayBuilder createArrayBuilder(Collection collection) { + return JsonProvider.provider().createArrayBuilder(collection); + } + + /** + * Creates a JSON object builder + * + * @return a JSON object builder + */ + public static JsonObjectBuilder createObjectBuilder() { + return JsonProvider.provider().createObjectBuilder(); + } + + /** + * Creates a JSON object builder, initialized with the specified object. + * + * @param object the initial object in the builder + * @return a JSON object builder + * + * @since 1.1 + */ + public static JsonObjectBuilder createObjectBuilder(JsonObject object) { + return JsonProvider.provider().createObjectBuilder(object); + } + + /** + * Creates a JSON object builder, initialized with the data from specified {@code map}. + * If the @{code map} contains {@link Optional}s then resulting JSON object builder + * contains the key from the {@code map} only if the {@link Optional} is not empty. + * + * @param map the initial object in the builder + * @return a JSON object builder + * @exception IllegalArgumentException if the value from the {@code map} cannot be converted + * to the corresponding {@link JsonValue} + * + * @since 1.1 + */ + public static JsonObjectBuilder createObjectBuilder(Map map) { + return JsonProvider.provider().createObjectBuilder(map); + } + + /** + * Creates JSON Pointer (RFC 6901) + * from given {@code jsonPointer} string. + *

    + *
  • An empty {@code jsonPointer} string defines a reference to the target itself.
  • + *
  • If the {@code jsonPointer} string is non-empty, it must be a sequence of '{@code /}' prefixed tokens.
  • + *
+ * + * @param jsonPointer the valid escaped JSON Pointer string + * @throws NullPointerException if {@code jsonPointer} is {@code null} + * @throws JsonException if {@code jsonPointer} is not a valid JSON Pointer + * @return a JSON Pointer + * + * @since 1.1 + */ + public static JsonPointer createPointer(String jsonPointer) { + return JsonProvider.provider().createPointer(jsonPointer); + } + + /** + * Creates a JSON Patch builder (RFC 6902). + * + * @return a JSON Patch builder + * + * @since 1.1 + */ + public static JsonPatchBuilder createPatchBuilder() { + return JsonProvider.provider().createPatchBuilder(); + } + + /** + * Creates a JSON Patch builder + * (RFC 6902), + * initialized with the specified operations. + * + * @param array the initial patch operations + * @return a JSON Patch builder + * + * @since 1.1 + */ + public static JsonPatchBuilder createPatchBuilder(JsonArray array) { + return JsonProvider.provider().createPatchBuilder(array); + } + + /** + * Creates a JSON Patch (RFC 6902) + * from the specified operations. + * + * @param array patch operations + * @return a JSON Patch + * + * @since 1.1 + */ + public static JsonPatch createPatch(JsonArray array) { + return JsonProvider.provider().createPatch(array); + } + + /** + * Generates a JSON Patch (RFC 6902) + * from the source and target {@code JsonStructure}. + * The generated JSON Patch need not be unique. + * + * @param source the source + * @param target the target, must be the same type as the source + * @return a JSON Patch which when applied to the source, yields the target + * + * @since 1.1 + */ + public static JsonPatch createDiff(JsonStructure source, JsonStructure target) { + return JsonProvider.provider().createDiff(source, target); + } + + /** + * Creates JSON Merge Patch (RFC 7396) + * from specified {@code JsonValue}. + * + * @param patch the patch + * @return a JSON Merge Patch + * + * @since 1.1 + */ + public static JsonMergePatch createMergePatch(JsonValue patch) { + return JsonProvider.provider().createMergePatch(patch); + } + + /** + * Generates a JSON Merge Patch (RFC 7396) + * from the source and target {@code JsonValue}s + * which when applied to the {@code source}, yields the {@code target}. + * + * @param source the source + * @param target the target + * @return a JSON Merge Patch + * + * @since 1.1 + */ + public static JsonMergePatch createMergeDiff(JsonValue source, JsonValue target) { + return JsonProvider.provider().createMergeDiff(source, target); + } + + /** + * Creates a builder factory for creating {@link JsonArrayBuilder} + * and {@link JsonObjectBuilder} objects. + * The factory is configured with the specified map of provider specific + * configuration properties. Provider implementations should ignore any + * unsupported configuration properties specified in the map. + * + * @param config a map of provider specific properties to configure the + * JSON builders. The map may be empty or null + * @return a JSON builder factory + */ + public static JsonBuilderFactory createBuilderFactory( + Map config) { + return JsonProvider.provider().createBuilderFactory(config); + } + + /** + * Creates a JsonString. + * + * @param value a JSON string + * @return the JsonString for the string + * + * @since 1.1 + */ + public static JsonString createValue(String value) { + return JsonProvider.provider().createValue(value); + } + + /** + * Creates a JsonNumber. + * + * @param value a JSON number + * @return the JsonNumber for the number + * + * @since 1.1 + */ + public static JsonNumber createValue(int value) { + return JsonProvider.provider().createValue(value); + } + + /** + * Creates a JsonNumber. + * + * @param value a JSON number + * @return the JsonNumber for the number + * + * @since 1.1 + */ + public static JsonNumber createValue(long value) { + return JsonProvider.provider().createValue(value); + } + + /** + * Creates a JsonNumber. + * + * @param value a JSON number + * @return the JsonNumber for the number + * + * @since 1.1 + */ + public static JsonNumber createValue(double value) { + return JsonProvider.provider().createValue(value); + } + + /** + * Creates a JsonNumber. + * + * @param value a JSON number + * @return the JsonNumber for the number + * + * @since 1.1 + */ + public static JsonNumber createValue(BigDecimal value) { + return JsonProvider.provider().createValue(value); + } + + /** + * Creates a JsonNumber. + * + * @param value a JSON number + * @return the JsonNumber for the number + * + * @since 1.1 + */ + public static JsonNumber createValue(BigInteger value) { + return JsonProvider.provider().createValue(value); + } + + /** + * Encodes (escapes) a passed string as defined by RFC 6901. + * This method doesn't validate the passed JSON-pointer string. + * + * @param pointer the JSON-pointer string to encode + * @return encoded JSON-pointer string + * + * @since 1.1 + */ + public static String encodePointer(String pointer) { + return pointer.replace("~", "~0").replace("/", "~1"); + } + + /** + * Decodes a passed JSON-pointer string as defined by RFC 6901. + * This method doesn't validate the passed JSON-pointer string. + * + * @param escaped the JSON-pointer string to decode + * @return decoded JSON-pointer string + * + * @since 1.1 + */ + public static String decodePointer(String escaped) { + return escaped.replace("~1", "/").replace("~0", "~"); + } + +} diff --git a/api/src/main/java/javax/json/JsonArray.java b/api/src/main/java/javax/json/JsonArray.java new file mode 100644 index 00000000..a7bfae43 --- /dev/null +++ b/api/src/main/java/javax/json/JsonArray.java @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2011, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json; + +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * {@code JsonArray} represents an immutable JSON array + * (an ordered sequence of zero or more values). + * It also provides an unmodifiable list view of the values in the array. + * + *

A {@code JsonArray} object can be created by reading JSON data from + * an input source or it can be built from scratch using an array builder + * object. + * + *

The following example demonstrates how to create a {@code JsonArray} + * object from an input source using the method {@link JsonReader#readArray()}: + *


+ * JsonReader jsonReader = Json.createReader(...);
+ * JsonArray array = jsonReader.readArray();
+ * jsonReader.close();
+ * 
+ * + *

The following example demonstrates how to build an empty JSON array + * using the class {@link JsonArrayBuilder}: + *


+ * JsonArray array = Json.createArrayBuilder().build();
+ * 
+ * + *

The example code below demonstrates how to create the following JSON array: + *


+ * [
+ *     { "type": "home", "number": "212 555-1234" },
+ *     { "type": "fax", "number": "646 555-4567" }
+ * ]
+ * 
+ *

+ * JsonArray value = Json.createArrayBuilder()
+ *     .add(Json.createObjectBuilder()
+ *         .add("type", "home")
+ *         .add("number", "212 555-1234"))
+ *     .add(Json.createObjectBuilder()
+ *         .add("type", "fax")
+ *         .add("number", "646 555-4567"))
+ *     .build();
+ * 
+ * + *

The following example demonstrates how to write a {@code JsonArray} object + * as JSON data: + *


+ * JsonArray arr = ...;
+ * JsonWriter writer = Json.createWriter(...)
+ * writer.writeArray(arr);
+ * writer.close();
+ * 
+ * + *

The values in a {@code JsonArray} can be of the following types: + * {@link JsonObject}, {@link JsonArray}, + * {@link JsonString}, {@link JsonNumber}, {@link JsonValue#TRUE}, + * {@link JsonValue#FALSE}, and {@link JsonValue#NULL}. + * {@code JsonArray} provides various accessor methods to access the values + * in an array. + * + *

The following example shows how to obtain the home phone number + * "212 555-1234" from the array built in the previous example: + *


+ * JsonObject home = array.getJsonObject(0);
+ * String number = home.getString("number");
+ * 
+ * + *

{@code JsonArray} instances are list objects that provide read-only + * access to the values in the JSON array. Any attempt to modify the list, + * whether directly or using its collection views, results in an + * {@code UnsupportedOperationException}. + */ +public interface JsonArray extends JsonStructure, List { + + /** + * Returns the object value at the specified position in this array. + * This is a convenience method for {@code (JsonObject)get(index)}. + * + * @param index index of the value to be returned + * @return the value at the specified position in this array + * @throws IndexOutOfBoundsException if the index is out of range + * @throws ClassCastException if the value at the specified position is not + * assignable to the JsonObject type + */ + JsonObject getJsonObject(int index); + + /** + * Returns the array value at the specified position in this array. + * This is a convenience method for {@code (JsonArray)get(index)}. + * + * @param index index of the value to be returned + * @return the value at the specified position in this array + * @throws IndexOutOfBoundsException if the index is out of range + * @throws ClassCastException if the value at the specified position is not + * assignable to the JsonArray type + */ + JsonArray getJsonArray(int index); + + /** + * Returns the number value at the specified position in this array. + * This is a convenience method for {@code (JsonNumber)get(index)}. + * + * @param index index of the value to be returned + * @return the value at the specified position in this array + * @throws IndexOutOfBoundsException if the index is out of range + * @throws ClassCastException if the value at the specified position is not + * assignable to the JsonNumber type + */ + JsonNumber getJsonNumber(int index); + + /** + * Returns the string value at ths specified position in this array. + * This is a convenience method for {@code (JsonString)get(index)}. + * + * @param index index of the value to be returned + * @return the value at the specified position in this array + * @throws IndexOutOfBoundsException if the index is out of range + * @throws ClassCastException if the value at the specified position is not + * assignable to the JsonString type + */ + JsonString getJsonString(int index); + + /** + * Returns a list view of the specified type for the array. This method + * does not verify if there is a value of wrong type in the array. Providing + * this typesafe view dynamically may cause a program fail with a + * {@code ClassCastException}, if there is a value of wrong type in this + * array. Unfortunately, the exception can occur at any time after this + * method returns. + * + * @param The type of the List for the array + * @param clazz a JsonValue type + * @return a list view of the specified type + */ + List getValuesAs(Class clazz); + + /** + * Returns a list view for the array. The value and the type of the elements + * in the list is specified by the {@code func} argument. + *

This method can be used to obtain a list of the unwrapped types, such as + *

{@code
+     *     List strings = ary1.getValuesAs(JsonString::getString);
+     *     List ints = ary2.getValuesAs(JsonNumber::intValue);
+     * } 
+ * or a list of simple projections, such as + *
 {@code
+     *     List stringsizes = ary1.getValueAs((JsonString v)->v.getString().length();
+     * } 
+ * @param The element type (must be a subtype of JsonValue) of this JsonArray. + * @param The element type of the returned List + * @param func The function that maps the elements of this JsonArray to the target elements. + * @return A List of the specified values and type. + * @throws ClassCastException if the {@code JsonArray} contains a value of wrong type + * + * @since 1.1 + */ + default List getValuesAs(Function func) { + @SuppressWarnings("unchecked") + Stream stream = (Stream) stream(); + return stream.map(func).collect(Collectors.toList()); + } + + /** + * A convenience method for + * {@code getJsonString(index).getString()}. + * + * @param index index of the {@code JsonString} value + * @return the String value at the specified position + * @throws IndexOutOfBoundsException if the index is out of range + * @throws ClassCastException if the value at the specified position is not + * assignable to {@code JsonString} + */ + String getString(int index); + + /** + * Returns the {@code String} value of {@code JsonString} at the specified + * position in this JSON array values. If {@code JsonString} is found, + * its {@link javax.json.JsonString#getString()} is returned. Otherwise, + * the specified default value is returned. + * + * @param index index of the {@code JsonString} value + * @param defaultValue the String to return if the {@code JsonValue} at the + * specified position is not a {@code JsonString} + * @return the String value at the specified position in this array, + * or the specified default value + */ + String getString(int index, String defaultValue); + + /** + * A convenience method for + * {@code getJsonNumber(index).intValue()}. + * + * @param index index of the {@code JsonNumber} value + * @return the int value at the specified position + * @throws IndexOutOfBoundsException if the index is out of range + * @throws ClassCastException if the value at the specified position is not + * assignable to {@code JsonNumber} + */ + int getInt(int index); + + /** + * Returns the int value of the {@code JsonNumber} at the specified position. + * If the value at that position is a {@code JsonNumber}, + * this method returns {@link javax.json.JsonNumber#intValue()}. Otherwise + * this method returns the specified default value. + * + * @param index index of the {@code JsonNumber} value + * @param defaultValue the int value to return if the {@code JsonValue} at + * the specified position is not a {@code JsonNumber} + * @return the int value at the specified position in this array, + * or the specified default value + */ + int getInt(int index, int defaultValue); + + /** + * Returns the boolean value at the specified position. + * If the value at the specified position is {@code JsonValue.TRUE} + * this method returns {@code true}. If the value at the specified position + * is {@code JsonValue.FALSE} this method returns {@code false}. + * + * @param index index of the JSON boolean value + * @return the boolean value at the specified position + * @throws IndexOutOfBoundsException if the index is out of range + * @throws ClassCastException if the value at the specified position is not + * assignable to {@code JsonValue.TRUE} or {@code JsonValue.FALSE} + */ + boolean getBoolean(int index); + + /** + * Returns the boolean value at the specified position. + * If the value at the specified position is {@code JsonValue.TRUE} + * this method returns {@code true}. If the value at the specified position + * is {@code JsonValue.FALSE} this method returns {@code false}. + * Otherwise this method returns the specified default value. + * + * @param index index of the JSON boolean value + * @param defaultValue the boolean value to return if the {@code JsonValue} + * at the specified position is neither TRUE nor FALSE + * @return the boolean value at the specified position, + * or the specified default value + */ + boolean getBoolean(int index, boolean defaultValue); + + /** + * Returns {@code true} if the value at the specified location in this + * array is {@code JsonValue.NULL}. + * + * @param index index of the JSON null value + * @return return true if the value at the specified location is + * {@code JsonValue.NULL}, otherwise false + * @throws IndexOutOfBoundsException if the index is out of range + */ + boolean isNull(int index); +} diff --git a/api/src/main/java/javax/json/JsonArrayBuilder.java b/api/src/main/java/javax/json/JsonArrayBuilder.java new file mode 100644 index 00000000..06738294 --- /dev/null +++ b/api/src/main/java/javax/json/JsonArrayBuilder.java @@ -0,0 +1,622 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json; + +import java.math.BigDecimal; +import java.math.BigInteger; + +/** + * A builder for creating {@link JsonArray} models from scratch, and for + * modifying a existing {@code JsonArray}. + *

A {@code JsonArrayBuilder} can start with an empty or a non-empty + * JSON array model. This interface provides methods to add, insert, remove + * and replace values in the JSON array model.

+ *

Methods in this class can be chained to perform multiple values to + * the array.

+ * + *

The class {@link javax.json.Json} contains methods to create the builder + * object. The example code below shows how to build an empty {@code JsonArray} + * instance. + *

+ * 
+ * JsonArray array = Json.createArrayBuilder().build();
+ * 
+ * 
+ * + *

The class {@link JsonBuilderFactory} also contains methods to create + * {@code JsonArrayBuilder} instances. A factory instance can be used to create + * multiple builder instances with the same configuration. This the preferred + * way to create multiple instances. + * + * The example code below shows how to build a {@code JsonArray} object + * that represents the following JSON array: + * + *

+ * 
+ * [
+ *     { "type": "home", "number": "212 555-1234" },
+ *     { "type": "fax", "number": "646 555-4567" }
+ * ]
+ * 
+ * 
+ * + *

The following code creates the JSON array above: + * + *

+ * 
+ * JsonBuilderFactory factory = Json.createBuilderFactory(config);
+ * JsonArray value = factory.createArrayBuilder()
+ *     .add(factory.createObjectBuilder()
+ *         .add("type", "home")
+ *         .add("number", "212 555-1234"))
+ *     .add(factory.createObjectBuilder()
+ *         .add("type", "fax")
+ *         .add("number", "646 555-4567"))
+ *     .build();
+ * 
+ * 
+ * + *

This class does not allow null to be used as a + * value while building the JSON array + * + * @see JsonObjectBuilder + */ +public interface JsonArrayBuilder { + + /** + * Adds a value to the array. + * + * @param value the JSON value + * @return this array builder + * @throws NullPointerException if the specified value is null + */ + JsonArrayBuilder add(JsonValue value); + + /** + * Adds a value to the array as a {@link JsonString}. + * + * @param value the string value + * @return this array builder + * @throws NullPointerException if the specified value is null + */ + JsonArrayBuilder add(String value); + + /** + * Adds a value to the array as a {@link JsonNumber}. + * + * @param value the number value + * @return this array builder + * @throws NullPointerException if the specified value is null + * + * @see JsonNumber + */ + JsonArrayBuilder add(BigDecimal value); + + /** + * Adds a value to the array as a {@link JsonNumber}. + * + * @param value the number value + * @return this array builder + * @throws NullPointerException if the specified value is null + * + * @see JsonNumber + */ + JsonArrayBuilder add(BigInteger value); + + /** + * Adds a value to the array as a {@link JsonNumber}. + * + * @param value the number value + * @return this array builder + * + * @see JsonNumber + */ + JsonArrayBuilder add(int value); + + /** + * Adds a value to the array as a {@link JsonNumber}. + * + * @param value the number value + * @return this array builder + * + * @see JsonNumber + */ + JsonArrayBuilder add(long value); + + /** + * Adds a value to the array as a {@link JsonNumber}. + * + * @param value the number value + * @return this array builder + * @throws NumberFormatException if the value is Not-a-Number (NaN) or + * infinity + * + * @see JsonNumber + */ + JsonArrayBuilder add(double value); + + /** + * Adds a {@link JsonValue#TRUE} or {@link JsonValue#FALSE} value to the + * array. + * + * @param value the boolean value + * @return this array builder + */ + JsonArrayBuilder add(boolean value); + + /** + * Adds a {@link JsonValue#NULL} value to the array. + * + * @return this array builder + */ + JsonArrayBuilder addNull(); + + /** + * Adds a {@link JsonObject} from an object builder to the array. + * + * @param builder the object builder + * @return this array builder + * @throws NullPointerException if the specified builder is null + */ + JsonArrayBuilder add(JsonObjectBuilder builder); + + /** + * Adds a {@link JsonArray} from an array builder to the array. + * + * @param builder the array builder + * @return this array builder + * @throws NullPointerException if the specified builder is null + */ + JsonArrayBuilder add(JsonArrayBuilder builder); + + /** + * Adds all elements of the array in the specified array builder to the array. + * + * @param builder the array builder + * @return this array builder + * @throws NullPointerException if the specified builder is null + * + @since 1.1 + */ + default JsonArrayBuilder addAll(JsonArrayBuilder builder) { + throw new UnsupportedOperationException(); + } + + /** + * Inserts a value to the array at the specified position. Shifts the value + * currently at that position (if any) and any subsequent values to the right + * (adds one to their indices). Index starts with 0. + * + * @param index the position in the array + * @param value the JSON value + * @return this array builder + * @throws NullPointerException if the specified value is null + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index > array size)} + * + * @since 1.1 + */ + default JsonArrayBuilder add(int index, JsonValue value) { + throw new UnsupportedOperationException(); + } + + /** + * Adds a value to the array as a {@link JsonString} at the specified position. + * Shifts the value currently at that position (if any) and any subsequent values + * to the right (adds one to their indices). Index starts with 0. + * + * @param index the position in the array + * @param value the string value + * @return this array builder + * @throws NullPointerException if the specified value is null + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index > array size)} + * + * @since 1.1 + */ + default JsonArrayBuilder add(int index, String value) { + throw new UnsupportedOperationException(); + } + + /** + * Adds a value to the array as a {@link JsonNumber} at the specified position. + * Shifts the value currently at that position (if any) and any subsequent values + * to the right (adds one to their indices). Index starts with 0. + * + * @param index the position in the array + * @param value the number value + * @return this array builder + * @throws NullPointerException if the specified value is null + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index > array size)} + * + * @see JsonNumber + * + * @since 1.1 + */ + default JsonArrayBuilder add(int index, BigDecimal value) { + throw new UnsupportedOperationException(); + } + + /** + * Adds a value to the array as a {@link JsonNumber} at the specified position. + * Shifts the value currently at that position (if any) and any subsequent values + * to the right (adds one to their indices). Index starts with 0. + * + * @param index the position in the array + * @param value the number value + * @return this array builder + * @throws NullPointerException if the specified value is null + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index > array size)} + * + * @see JsonNumber + * + * @since 1.1 + */ + default JsonArrayBuilder add(int index, BigInteger value) { + throw new UnsupportedOperationException(); + } + + /** + * Adds a value to the array as a {@link JsonNumber} at the specified position. + * Shifts the value currently at that position (if any) and any subsequent values + * to the right (adds one to their indices). Index starts with 0. + * + * @param index the position in the array + * @param value the number value + * @return this array builder + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index > array size)} + * + * @see JsonNumber + * + * @since 1.1 + */ + default JsonArrayBuilder add(int index, int value) { + throw new UnsupportedOperationException(); + } + + /** + * Adds a value to the array as a {@link JsonNumber} at the specified position. + * Shifts the value currently at that position (if any) and any subsequent values + * to the right (adds one to their indices). Index starts with 0. + * + * @param index the position in the array + * @param value the number value + * @return this array builder + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index > array size)} + * + * @see JsonNumber + * + * @since 1.1 + */ + default JsonArrayBuilder add(int index, long value) { + throw new UnsupportedOperationException(); + } + + /** + * Adds a value to the array as a {@link JsonNumber} at the specified position. + * Shifts the value currently at that position (if any) and any subsequent values + * to the right (adds one to their indices). Index starts with 0. + * + * @param index the position in the array + * @param value the number value + * @return this array builder + * @throws NumberFormatException if the value is Not-a-Number (NaN) or + * infinity + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index > array size)} + * + * @see JsonNumber + * + * @since 1.1 + */ + default JsonArrayBuilder add(int index, double value) { + throw new UnsupportedOperationException(); + } + + /** + * Adds a {@link JsonValue#TRUE} or {@link JsonValue#FALSE} value to the + * array at the specified position. + * Shifts the value currently at that position (if any) and any subsequent values + * to the right (adds one to their indices). Index starts with 0. + * + * @param index the position in the array + * @param value the boolean value + * @return this array builder + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index > array size)} + * + * @since 1.1 + */ + default JsonArrayBuilder add(int index, boolean value) { + throw new UnsupportedOperationException(); + } + + /** + * Adds a {@link JsonValue#NULL} value to the array at the specified position. + * Shifts the value currently at that position (if any) and any subsequent values + * to the right (adds one to their indices). Index starts with 0. + * + * @param index the position in the array + * @return this array builder + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index > array size)} + * + * @since 1.1 + */ + default JsonArrayBuilder addNull(int index) { + return add(index, JsonValue.NULL); + } + + /** + * Adds a {@link JsonObject} from an object builder to the array at the specified position. + * Shifts the value currently at that position (if any) and any subsequent values + * to the right (adds one to their indices). Index starts with 0. + * + * @param index the position in the array + * @param builder the object builder + * @return this array builder + * @throws NullPointerException if the specified builder is null + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index > array size)} + * + * @since 1.1 + */ + default JsonArrayBuilder add(int index, JsonObjectBuilder builder) { + throw new UnsupportedOperationException(); + } + + /** + * Adds a {@link JsonArray} from an array builder to the array at the specified position. + * Shifts the value currently at that position (if any) and any subsequent values + * to the right (adds one to their indices). Index starts with 0. + * + * @param index the position in the array + * @param builder the array builder + * @return this array builder + * @throws NullPointerException if the specified builder is null + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index > array size)} + * + * @since 1.1 + */ + default JsonArrayBuilder add(int index, JsonArrayBuilder builder) { + throw new UnsupportedOperationException(); + } + + /** + * Replaces a value in the array with the specified value at the + * specified position. + * + * @param index the position in the array + * @param value the JSON value + * @return this array builder + * @throws NullPointerException if the specified value is null + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index >= array size)} + * + * @since 1.1 + */ + default JsonArrayBuilder set(int index, JsonValue value) { + throw new UnsupportedOperationException(); + } + + /** + * Replaces a value in the array with the specified value as a + * {@link JsonString} at the specified position. + * + * @param index the position in the array + * @param value the string value + * @return this array builder + * @throws NullPointerException if the specified value is null + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index >= array size)} + * + * @since 1.1 + */ + default JsonArrayBuilder set(int index, String value) { + throw new UnsupportedOperationException(); + } + + /** + * Replaces a value in the array with the specified value as a + * {@link JsonNumber} at the specified position. + * + * @param index the position in the array + * @param value the number value + * @return this array builder + * @throws NullPointerException if the specified value is null + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index >= array size)} + * + * @see JsonNumber + * + * @since 1.1 + */ + default JsonArrayBuilder set(int index, BigDecimal value) { + throw new UnsupportedOperationException(); + } + + /** + * Replaces a value in the array with the specified value as a + * {@link JsonNumber} at the specified position. + * + * @param index the position in the array + * @param value the number value + * @return this array builder + * @throws NullPointerException if the specified value is null + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index >= array size)} + * + * @see JsonNumber + * + * @since 1.1 + */ + default JsonArrayBuilder set(int index, BigInteger value) { + throw new UnsupportedOperationException(); + } + + /** + * Replaces a value in the array with the specified value as a + * {@link JsonNumber} at the specified position. + * + * @param index the position in the array + * @param value the number value + * @return this array builder + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index >= array size)} + * + * @see JsonNumber + * + * @since 1.1 + */ + default JsonArrayBuilder set(int index, int value) { + throw new UnsupportedOperationException(); + } + + /** + * Replaces a value in the array with the specified value as a + * {@link JsonNumber} at the specified position. + * + * @param index the position in the array + * @param value the number value + * @return this array builder + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index >= array size)} + * + * @see JsonNumber + * + * @since 1.1 + */ + default JsonArrayBuilder set(int index, long value) { + throw new UnsupportedOperationException(); + } + + /** + * Replaces a value in the array with the specified value as a + * {@link JsonNumber} at the specified position. + * + * @param index the position in the array + * @param value the number value + * @return this array builder + * @throws NumberFormatException if the value is Not-a-Number (NaN) or + * infinity + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index >= array size)} + * + * @see JsonNumber + * + * @since 1.1 + */ + default JsonArrayBuilder set(int index, double value) { + throw new UnsupportedOperationException(); + } + + /** + * Replaces a value in the array with + * a {@link JsonValue#TRUE} or {@link JsonValue#FALSE} value + * at the specified position. + * + * @param index the position in the array + * @param value the boolean value + * @return this array builder + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index >= array size)} + * + * @since 1.1 + */ + default JsonArrayBuilder set(int index, boolean value) { + throw new UnsupportedOperationException(); + } + + /** + * Replaces a value in the array with + * a {@link JsonValue#NULL} value at the specified position. + * + * @param index the position in the array + * @return this array builder + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index >= array size)} + * + * @since 1.1 + */ + default JsonArrayBuilder setNull(int index) { + return set(index, JsonValue.NULL); + } + + /** + * Replaces a value in the array with the specified value as a + * {@link JsonObject} from an object builder at the specified position. + * + * @param index the position in the array + * @param builder the object builder + * @return this array builder + * @throws NullPointerException if the specified builder is null + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index >= array size)} + * + * @since 1.1 + */ + default JsonArrayBuilder set(int index, JsonObjectBuilder builder) { + throw new UnsupportedOperationException(); + } + + /** + * Replaces a value in the array with the specified value as a + * {@link JsonArray} from an array builder at the specified position. + * + * @param index the position in the array + * @param builder the array builder + * @return this array builder + * @throws NullPointerException if the specified builder is null + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index >= array size)} + * + * @since 1.1 + */ + default JsonArrayBuilder set(int index, JsonArrayBuilder builder) { + throw new UnsupportedOperationException(); + } + + /** + * Remove the value in the array at the specified position. + * Shift any subsequent values to the left (subtracts one from their + * indices. + * + * @param index the position in the array + * @return this array builder + * @throws IndexOutOfBoundsException if the index is out of range + * {@code (index < 0 || index >= array size)} + * + * @since 1.1 + */ + default JsonArrayBuilder remove(int index) { + throw new UnsupportedOperationException(); + } + + /** + * Returns the current array. + * + * @return the current JSON array + */ + JsonArray build(); + +} + diff --git a/api/src/main/java/javax/json/JsonBuilderFactory.java b/api/src/main/java/javax/json/JsonBuilderFactory.java new file mode 100644 index 00000000..fa67eaea --- /dev/null +++ b/api/src/main/java/javax/json/JsonBuilderFactory.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json; + +import java.util.Collection; +import java.util.Map; + +/** + * Factory to create {@link JsonObjectBuilder} and {@link JsonArrayBuilder} + * instances. If a factory instance is configured with some configuration, + * that would be used to configure the created builder instances. + * + *

+ * {@code JsonObjectBuilder} and {@code JsonArrayBuilder} can also be created + * using {@link Json}'s methods. If multiple builder instances are created, + * then creating them using a builder factory is preferred. + * + *

+ * For example: + *

+ * 
+ * JsonBuilderFactory factory = Json.createBuilderFactory(...);
+ * JsonArray value = factory.createArrayBuilder()
+ *     .add(factory.createObjectBuilder()
+ *         .add("type", "home")
+ *         .add("number", "212 555-1234"))
+ *     .add(factory.createObjectBuilder()
+ *         .add("type", "fax")
+ *         .add("number", "646 555-4567"))
+ *     .build();
+ * 
+ * 
+ * + *

All the methods in this class are safe for use by multiple concurrent + * threads. + */ +public interface JsonBuilderFactory { + + /** + * Creates a {@code JsonObjectBuilder} instance that is used to build + * {@link JsonObject}. + * + * @return a JSON object builder + */ + JsonObjectBuilder createObjectBuilder(); + + /** + * Creates a {@code JsonObjectBuilder} instance, initialized with an object. + * + * @param object the initial object in the builder + * @return a JSON object builder + * @throws NullPointerException if specified object is {@code null} + * + * @since 1.1 + */ + default JsonObjectBuilder createObjectBuilder(JsonObject object) { + throw new UnsupportedOperationException(); + } + + /** + * Creates a {@code JsonObjectBuilder} instance, initialized with the specified object. + * + * @param object the initial object in the builder + * @return a JSON object builder + * @throws NullPointerException if specified object is {@code null} + * + * @since 1.1 + */ + default JsonObjectBuilder createObjectBuilder(Map object) { + throw new UnsupportedOperationException(); + } + + /** + * Creates a {@code JsonArrayBuilder} instance that is used to build + * {@link JsonArray} + * + * @return a JSON array builder + */ + JsonArrayBuilder createArrayBuilder(); + + /** + * Creates a {@code JsonArrayBuilder} instance, initialized with an array. + * + * @param array the initial array in the builder + * @return a JSON array builder + * @throws NullPointerException if specified array is {@code null} + * + * @since 1.1 + */ + default JsonArrayBuilder createArrayBuilder(JsonArray array) { + throw new UnsupportedOperationException(); + } + + /** + * Creates a {@code JsonArrayBuilder} instance, + * initialized with the content of specified collection. + * + * @param collection the initial data for the builder + * @return a JSON array builder + * @throws NullPointerException if specified collection is {@code null} + * + * @since 1.1 + */ + default JsonArrayBuilder createArrayBuilder(Collection collection) { + throw new UnsupportedOperationException(); + } + + /** + * Returns read-only map of supported provider specific configuration + * properties that are used to configure the created JSON builders. + * If there are any specified configuration properties that are not + * supported by the provider, they won't be part of the returned map. + * + * @return a map of supported provider specific properties that are used + * to configure the builders. The map be empty but not null. + */ + Map getConfigInUse(); + +} diff --git a/api/src/main/java/javax/json/JsonException.java b/api/src/main/java/javax/json/JsonException.java new file mode 100644 index 00000000..329974d2 --- /dev/null +++ b/api/src/main/java/javax/json/JsonException.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2011, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json; + +/** + * JsonException indicates that some exception happened during + * JSON processing. + */ +public class JsonException extends RuntimeException { + + /** + * Constructs a new runtime exception with the specified detail message. + * The cause is not initialized, and may subsequently be initialized by a + * call to {@link #initCause}. + * + * @param message the detail message. The detail message is saved for + * later retrieval by the {@link #getMessage()} method. + */ + public JsonException(String message) { + super(message); + } + + /** + * Constructs a new runtime exception with the specified detail message and + * cause.

Note that the detail message associated with + * {@code cause} is not automatically incorporated in + * this runtime exception's detail message. + * + * @param message the detail message (which is saved for later retrieval + * by the {@link #getMessage()} method). + * @param cause the cause (which is saved for later retrieval by the + * {@link #getCause()} method). (A null value is + * permitted, and indicates that the cause is nonexistent or + * unknown.) + */ + public JsonException(String message, Throwable cause) { + super(message, cause); + } + +} + diff --git a/api/src/main/java/javax/json/JsonMergePatch.java b/api/src/main/java/javax/json/JsonMergePatch.java new file mode 100644 index 00000000..08012784 --- /dev/null +++ b/api/src/main/java/javax/json/JsonMergePatch.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json; + +/** + *

This interface represents an implementation of a JSON Merge Patch + * as defined by RFC 7396. + *

+ *

A {@code JsonMergePatch} can be instantiated with {@link Json#createMergePatch(JsonValue)} + * by specifying the patch operations in a JSON Merge Patch or using {@link Json#createMergeDiff(JsonValue, JsonValue)} + * to create a JSON Merge Patch based on the difference between two {@code JsonValue}s. + *

+ * The following illustrates both approaches. + *

1. Construct a JsonMergePatch with an existing JSON Merge Patch. + *

{@code
+ *   JsonValue contacts = ... ; // The target to be patched
+ *   JsonValue patch = ...  ; // JSON Merge Patch
+ *   JsonMergePatch mergePatch = Json.createMergePatch(patch);
+ *   JsonValue result = mergePatch.apply(contacts);
+ * } 
+ * 2. Construct a JsonMergePatch from a difference between two {@code JsonValue}s. + *
{@code
+ *   JsonValue source = ... ; // The source object
+ *   JsonValue target = ... ; // The modified object
+ *   JsonMergePatch mergePatch = Json.createMergeDiff(source, target); // The diff between source and target in a Json Merge Patch format
+ * } 
+ * + * @see RFC 7396 + * + * @since 1.1 + */ +public interface JsonMergePatch { + + /** + * Applies the JSON Merge Patch to the specified {@code target}. + * The target is not modified by the patch. + * + * @param target the target to apply the merge patch + * @return the transformed target after the patch + */ + JsonValue apply(JsonValue target); + + /** + * Returns the {@code JsonMergePatch} as {@code JsonValue}. + * + * @return this {@code JsonMergePatch} as {@code JsonValue} + */ + JsonValue toJsonValue(); +} diff --git a/api/src/main/java/javax/json/JsonNumber.java b/api/src/main/java/javax/json/JsonNumber.java new file mode 100644 index 00000000..6173c8f8 --- /dev/null +++ b/api/src/main/java/javax/json/JsonNumber.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2011, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json; + +import java.math.BigDecimal; +import java.math.BigInteger; + +/** + * An immutable JSON number value. + * + *

+ * Implementations may use a {@link BigDecimal} object to store the numeric + * value internally. + * The {@code BigDecimal} object can be constructed from the following types: + * int {@link BigDecimal#BigDecimal(int)}, + * long {@link BigDecimal#BigDecimal(long)}, + * BigInteger {@link BigDecimal#BigDecimal(BigInteger)}, + * double {@link BigDecimal#valueOf(double)}, and + * String {@link BigDecimal#BigDecimal(String)}. + * Some of the method semantics in this class are defined using the + * {@code BigDecimal} semantics. + */ +public interface JsonNumber extends JsonValue { + + /** + * Returns true if this JSON number is a integral number. This method + * semantics are defined using {@code bigDecimalValue().scale()}. If the + * scale is zero, then it is considered integral type. This integral type + * information can be used to invoke an appropriate accessor method to + * obtain a numeric value as in the following example: + * + *

+     * 
+     * JsonNumber num = ...
+     * if (num.isIntegral()) {
+     *     num.longValue();     // or other methods to get integral value
+     * } else {
+     *     num.doubleValue();   // or other methods to get decimal number value
+     * }
+     * 
+     * 
+ * + * @return true if this number is a integral number, otherwise false + */ + boolean isIntegral(); + + /** + * Returns this JSON number as an {@code int}. Note that this conversion + * can lose information about the overall magnitude and precision of the + * number value as well as return a result with the opposite sign. + * + * @return an {@code int} representation of the JSON number + * @see java.math.BigDecimal#intValue() + */ + int intValue(); + + /** + * Returns this JSON number as an {@code int}. + * + * @return an {@code int} representation of the JSON number + * @throws ArithmeticException if the number has a nonzero fractional + * part or if it does not fit in an {@code int} + * @see java.math.BigDecimal#intValueExact() + */ + int intValueExact(); + + /** + * Returns this JSON number as a {@code long}. Note that this conversion + * can lose information about the overall magnitude and precision of the + * number value as well as return a result with the opposite sign. + * + * @return a {@code long} representation of the JSON number. + * @see java.math.BigDecimal#longValue() + */ + long longValue(); + + /** + * Returns this JSON number as a {@code long}. + * + * @return a {@code long} representation of the JSON number + * @throws ArithmeticException if the number has a non-zero fractional + * part or if it does not fit in a {@code long} + * @see java.math.BigDecimal#longValueExact() + */ + long longValueExact(); + + /** + * Returns this JSON number as a {@link BigInteger} object. This is a + * a convenience method for {@code bigDecimalValue().toBigInteger()}. + * Note that this conversion can lose information about the overall + * magnitude and precision of the number value as well as return a result + * with the opposite sign. + * + * @return a {@code BigInteger} representation of the JSON number. + * @see java.math.BigDecimal#toBigInteger() + */ + BigInteger bigIntegerValue(); + + /** + * Returns this JSON number as a {@link BigInteger} object. This is a + * convenience method for {@code bigDecimalValue().toBigIntegerExact()}. + * + * @return a {@link BigInteger} representation of the JSON number + * @throws ArithmeticException if the number has a nonzero fractional part + * @see java.math.BigDecimal#toBigIntegerExact() + */ + BigInteger bigIntegerValueExact(); + + /** + * Returns this JSON number as a {@code double}. This is a + * a convenience method for {@code bigDecimalValue().doubleValue()}. + * Note that this conversion can lose information about the overall + * magnitude and precision of the number value as well as return a result + * with the opposite sign. + * + * @return a {@code double} representation of the JSON number + * @see java.math.BigDecimal#doubleValue() + */ + double doubleValue(); + + /** + * Returns this JSON number as a {@link BigDecimal} object. + * + * @return a {@link BigDecimal} representation of the JSON number + */ + BigDecimal bigDecimalValue(); + + /** + * Returns this JSON number as a {@link Number} object. + * + * @return a {@link Number} representation of the JSON number + * + * @since 1.1 + */ + default Number numberValue() { + throw new UnsupportedOperationException(); + } + + /** + * Returns a JSON text representation of the JSON number. The + * representation is equivalent to {@link BigDecimal#toString()}. + * + * @return JSON text representation of the number + */ + @Override + String toString(); + + /** + * Compares the specified object with this {@code JsonNumber} object for + * equality. Returns {@code true} if and only if the type of the specified + * object is also {@code JsonNumber} and their {@link #bigDecimalValue()} + * objects are equal + * + * @param obj the object to be compared for equality with + * this {@code JsonNumber} + * @return {@code true} if the specified object is equal to this + * {@code JsonNumber} + */ + @Override + boolean equals(Object obj); + + /** + * Returns the hash code value for this {@code JsonNumber} object. The + * hash code of a {@code JsonNumber} object is defined as the hash code of + * its {@link #bigDecimalValue()} object. + * + * @return the hash code value for this {@code JsonNumber} object + */ + @Override + int hashCode(); + +} diff --git a/api/src/main/java/javax/json/JsonObject.java b/api/src/main/java/javax/json/JsonObject.java new file mode 100644 index 00000000..704243c1 --- /dev/null +++ b/api/src/main/java/javax/json/JsonObject.java @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2011, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json; + +import java.util.Map; + +/** + * {@code JsonObject} class represents an immutable JSON object value + * (an unordered collection of zero or more name/value pairs). + * It also provides unmodifiable map view to the JSON object + * name/value mappings. + * + *

A JsonObject instance can be created from an input source using + * {@link JsonReader#readObject()}. For example: + *


+ * JsonReader jsonReader = Json.createReader(...);
+ * JsonObject object = jsonReader.readObject();
+ * jsonReader.close();
+ * 
+ * + * It can also be built from scratch using a {@link JsonObjectBuilder}. + * + *

For example 1: An empty JSON object can be built as follows: + *


+ * JsonObject object = Json.createObjectBuilder().build();
+ * 
+ * + * For example 2: The following JSON + *

+ * {
+ *     "firstName": "John", "lastName": "Smith", "age": 25,
+ *     "address" : {
+ *         "streetAddress": "21 2nd Street",
+ *         "city": "New York",
+ *         "state": "NY",
+ *         "postalCode": "10021"
+ *     },
+ *     "phoneNumber": [
+ *         { "type": "home", "number": "212 555-1234" },
+ *         { "type": "fax", "number": "646 555-4567" }
+ *     ]
+ * }
+ * 
+ * can be built using : + *

+ * JsonObject value = Json.createObjectBuilder()
+ *     .add("firstName", "John")
+ *     .add("lastName", "Smith")
+ *     .add("age", 25)
+ *     .add("address", Json.createObjectBuilder()
+ *         .add("streetAddress", "21 2nd Street")
+ *         .add("city", "New York")
+ *         .add("state", "NY")
+ *         .add("postalCode", "10021"))
+ *     .add("phoneNumber", Json.createArrayBuilder()
+ *         .add(Json.createObjectBuilder()
+ *             .add("type", "home")
+ *             .add("number", "212 555-1234"))
+ *         .add(Json.createObjectBuilder()
+ *             .add("type", "fax")
+ *             .add("number", "646 555-4567")))
+ *     .build();
+ * 
+ * + * {@code JsonObject} can be written to JSON as follows: + *

+ * JsonWriter writer = ...
+ * JsonObject obj = ...;
+ * writer.writeObject(obj);
+ * 
+ * + * {@code JsonObject} values can be {@link JsonObject}, {@link JsonArray}, + * {@link JsonString}, {@link JsonNumber}, {@link JsonValue#TRUE}, + * {@link JsonValue#FALSE}, {@link JsonValue#NULL}. These values can be + * accessed using various accessor methods. + * + *

In the above example 2, "John" can be got using + *


+ * String firstName = object.getString("firstName");
+ * 
+ * + * This map object provides read-only access to the JSON object data, + * and attempts to modify the map, whether direct or via its collection + * views, result in an {@code UnsupportedOperationException}. + * + *

The map object's iteration ordering is based on the order in which + * name/value pairs are added to the corresponding builder or the order + * in which name/value pairs appear in the corresponding stream. + */ +public interface JsonObject extends JsonStructure, Map { + + /** + * Returns the array value to which the specified name is mapped. + * This is a convenience method for {@code (JsonArray)get(name)} to + * get the value. + * + * @param name the name whose associated value is to be returned + * @return the array value to which the specified name is mapped, or + * {@code null} if this object contains no mapping for the name + * @throws ClassCastException if the value to which the specified name + * is mapped is not assignable to JsonArray type + */ + JsonArray getJsonArray(String name); + + /** + * Returns the object value to which the specified name is mapped. + * This is a convenience method for {@code (JsonObject)get(name)} to + * get the value. + * + * @param name the name whose associated value is to be returned + * @return the object value to which the specified name is mapped, or + * {@code null} if this object contains no mapping for the name + * @throws ClassCastException if the value to which the specified name + * is mapped is not assignable to JsonObject type + */ + JsonObject getJsonObject(String name); + + /** + * Returns the number value to which the specified name is mapped. + * This is a convenience method for {@code (JsonNumber)get(name)} to + * get the value. + * + * @param name the name whose associated value is to be returned + * @return the number value to which the specified name is mapped, or + * {@code null} if this object contains no mapping for the name + * @throws ClassCastException if the value to which the specified name + * is mapped is not assignable to JsonNumber type + */ + JsonNumber getJsonNumber(String name); + + /** + * Returns the string value to which the specified name is mapped. + * This is a convenience method for {@code (JsonString)get(name)} to + * get the value. + * + * @param name the name whose associated value is to be returned + * @return the string value to which the specified name is mapped, or + * {@code null} if this object contains no mapping for the name + * @throws ClassCastException if the value to which the specified name + * is mapped is not assignable to JsonString type + */ + JsonString getJsonString(String name); + + /** + * A convenience method for + * {@code getJsonString(name).getString()} + * + * @param name whose associated value is to be returned as String + * @return the String value to which the specified name is mapped + * @throws NullPointerException if the specified name doesn't have any + * mapping + * @throws ClassCastException if the value for specified name mapping + * is not assignable to JsonString + */ + String getString(String name); + + /** + * Returns the string value of the associated {@code JsonString} mapping + * for the specified name. If {@code JsonString} is found, then its + * {@link javax.json.JsonString#getString()} is returned. Otherwise, + * the specified default value is returned. + * + * @param name whose associated value is to be returned as String + * @param defaultValue a default value to be returned + * @return the string value of the associated mapping for the name, + * or the default value + */ + String getString(String name, String defaultValue); + + /** + * A convenience method for + * {@code getJsonNumber(name).intValue()} + * + * @param name whose associated value is to be returned as int + * @return the int value to which the specified name is mapped + * @throws NullPointerException if the specified name doesn't have any + * mapping + * @throws ClassCastException if the value for specified name mapping + * is not assignable to JsonNumber + */ + int getInt(String name); + + /** + * Returns the int value of the associated {@code JsonNumber} mapping + * for the specified name. If {@code JsonNumber} is found, then its + * {@link javax.json.JsonNumber#intValue()} is returned. Otherwise, + * the specified default value is returned. + * + * @param name whose associated value is to be returned as int + * @param defaultValue a default value to be returned + * @return the int value of the associated mapping for the name, + * or the default value + */ + int getInt(String name, int defaultValue); + + /** + * Returns the boolean value of the associated mapping for the specified + * name. If the associated mapping is JsonValue.TRUE, then returns true. + * If the associated mapping is JsonValue.FALSE, then returns false. + * + * @param name whose associated value is to be returned as boolean + * @return the boolean value to which the specified name is mapped + * @throws NullPointerException if the specified name doesn't have any + * mapping + * @throws ClassCastException if the value for specified name mapping + * is not assignable to JsonValue.TRUE or JsonValue.FALSE + */ + boolean getBoolean(String name); + + /** + * Returns the boolean value of the associated mapping for the specified + * name. If the associated mapping is JsonValue.TRUE, then returns true. + * If the associated mapping is JsonValue.FALSE, then returns false. + * Otherwise, the specified default value is returned. + * + * @param name whose associated value is to be returned as int + * @param defaultValue a default value to be returned + * @return the boolean value of the associated mapping for the name, + * or the default value + */ + boolean getBoolean(String name, boolean defaultValue); + + /** + * Returns {@code true} if the associated value for the specified name is + * {@code JsonValue.NULL}. + * + * @param name name whose associated value is checked + * @return return true if the associated value is {@code JsonValue.NULL}, + * otherwise false + * @throws NullPointerException if the specified name doesn't have any + * mapping + */ + boolean isNull(String name); + +} diff --git a/api/src/main/java/javax/json/JsonObjectBuilder.java b/api/src/main/java/javax/json/JsonObjectBuilder.java new file mode 100644 index 00000000..119cd75b --- /dev/null +++ b/api/src/main/java/javax/json/JsonObjectBuilder.java @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json; + +import java.math.BigDecimal; +import java.math.BigInteger; + +/** + * A builder for creating {@link JsonObject} models from scratch. This + * interface initializes an empty JSON object model and provides methods to add + * name/value pairs to the object model and to return the resulting object. + * The methods in this class can be chained to add multiple name/value pairs + * to the object. + * + *

The class {@link javax.json.Json} contains methods to create the builder + * object. The example code below shows how to build an empty {@code JsonObject} + * instance. + *

+ * 
+ * JsonObject object = Json.createObjectBuilder().build();
+ * 
+ * 
+ * + *

The class {@link JsonBuilderFactory} also contains methods to create + * {@code JsonObjectBuilder} instances. A factory instance can be used to create + * multiple builder instances with the same configuration. This the preferred + * way to create multiple instances. + * + * The example code below shows how to build a {@code JsonObject} model that + * represents the following JSON object: + * + *

+ * 
+ * {
+ *     "firstName": "John", "lastName": "Smith", "age": 25,
+ *     "address" : {
+ *         "streetAddress": "21 2nd Street",
+ *         "city": "New York",
+ *         "state": "NY",
+ *         "postalCode": "10021"
+ *     },
+ *     "phoneNumber": [
+ *         { "type": "home", "number": "212 555-1234" },
+ *         { "type": "fax", "number": "646 555-4567" }
+ *     ]
+ * }
+ * 
+ * 
+ * + *

The code to create the object shown above is the following: + * + *

+ * 
+ * JsonBuilderFactory factory = Json.createBuilderFactory(config);
+ * JsonObject value = factory.createObjectBuilder()
+ *     .add("firstName", "John")
+ *     .add("lastName", "Smith")
+ *     .add("age", 25)
+ *     .add("address", factory.createObjectBuilder()
+ *         .add("streetAddress", "21 2nd Street")
+ *         .add("city", "New York")
+ *         .add("state", "NY")
+ *         .add("postalCode", "10021"))
+ *     .add("phoneNumber", factory.createArrayBuilder()
+ *         .add(factory.createObjectBuilder()
+ *             .add("type", "home")
+ *             .add("number", "212 555-1234"))
+ *         .add(factory.createObjectBuilder()
+ *             .add("type", "fax")
+ *             .add("number", "646 555-4567")))
+ *     .build();
+ * 
+ * 
+ * + *

This class does not allow null to be used as a name or + * value while building the JSON object + * + * @see JsonArrayBuilder + */ +public interface JsonObjectBuilder { + + /** + * Adds a name/{@code JsonValue} pair to the JSON object associated with + * this object builder. If the object contains a mapping for the specified + * name, this method replaces the old value with the specified value. + * + * @param name name in the name/value pair + * @param value value in the name/value pair + * @return this object builder + * @throws NullPointerException if the specified name or value is null + */ + JsonObjectBuilder add(String name, JsonValue value); + + /** + * Adds a name/{@code JsonString} pair to the JSON object associated with + * this object builder. If the object contains a mapping for the specified + * name, this method replaces the old value with the specified value. + * + * @param name name in the name/value pair + * @param value value in the name/value pair + * @return this object builder + * @throws NullPointerException if the specified name or value is null + */ + JsonObjectBuilder add(String name, String value); + + /** + * Adds a name/{@code JsonNumber} pair to the JSON object associated with + * this object builder. If the object contains a mapping for the specified + * name, this method replaces the old value with the specified value. + * + * @param name name in the name/value pair + * @param value value in the name/value pair + * @return this object builder + * @throws NullPointerException if the specified name or value is null + * + * @see JsonNumber + */ + JsonObjectBuilder add(String name, BigInteger value); + + /** + * Adds a name/{@code JsonNumber} pair to the JSON object associated with + * this object builder. If the object contains a mapping for the specified + * name, this method replaces the old value with the specified value. + * + * @param name name in the name/value pair + * @param value value in the name/value pair + * @return this object builder + * @throws NullPointerException if the specified name or value is null + * + * @see JsonNumber + */ + JsonObjectBuilder add(String name, BigDecimal value); + + /** + * Adds a name/{@code JsonNumber} pair to the JSON object associated with + * this object builder. If the object contains a mapping for the specified + * name, this method replaces the old value with the specified value. + * + * @param name name in the name/value pair + * @param value value in the name/value pair + * @return this object builder + * @throws NullPointerException if the specified name is null + * + * @see JsonNumber + */ + JsonObjectBuilder add(String name, int value); + + /** + * Adds a name/{@code JsonNumber} pair to the JSON object associated with + * this object builder. If the object contains a mapping for the specified + * name, this method replaces the old value with the specified value. + * + * @param name name in the name/value pair + * @param value value in the name/value pair + * @return this object builder + * @throws NullPointerException if the specified name is null + * + * @see JsonNumber + */ + JsonObjectBuilder add(String name, long value); + + /** + * Adds a name/{@code JsonNumber} pair to the JSON object associated with + * this object builder. If the object contains a mapping for the specified + * name, this method replaces the old value with the specified value. + * + * @param name name in the name/value pair + * @param value value in the name/value pair + * @return this object builder + * @throws NumberFormatException if the value is Not-a-Number (NaN) or + * infinity + * @throws NullPointerException if the specified name is null + * + * @see JsonNumber + */ + JsonObjectBuilder add(String name, double value); + + /** + * Adds a name/{@code JsonValue#TRUE} or name/{@code JsonValue#FALSE} pair + * to the JSON object associated with this object builder. If the object + * contains a mapping for the specified name, this method replaces the old + * value with the specified value. + * + * @param name name in the name/value pair + * @param value value in the name/value pair + * @return this object builder + * @throws NullPointerException if the specified name is null + */ + JsonObjectBuilder add(String name, boolean value); + + /** + * Adds a name/{@code JsonValue#NULL} pair to the JSON object associated + * with this object builder where the value is {@code null}. + * If the object contains a mapping for the specified name, this method + * replaces the old value with {@code null}. + * + * @param name name in the name/value pair + * @return this object builder + * @throws NullPointerException if the specified name is null + */ + JsonObjectBuilder addNull(String name); + + /** + * Adds a name/{@code JsonObject} pair to the JSON object associated + * with this object builder. The value {@code JsonObject} is built from the + * specified object builder. If the object contains a mapping for the + * specified name, this method replaces the old value with the + * {@code JsonObject} from the specified object builder. + * + * @param name name in the name/value pair + * @param builder the value is the object associated with this builder + * @return this object builder + * @throws NullPointerException if the specified name or builder is null + */ + JsonObjectBuilder add(String name, JsonObjectBuilder builder); + + /** + * Adds a name/{@code JsonArray} pair to the JSON object associated with + * this object builder. The value {@code JsonArray} is built from the + * specified array builder. If the object contains a mapping for the + * specified name, this method replaces the old value with the + * {@code JsonArray} from the specified array builder. + * + * @param name the name in the name/value pair + * @param builder the value is the object array with this builder + * @return this object builder + * @throws NullPointerException if the specified name or builder is null + */ + JsonObjectBuilder add(String name, JsonArrayBuilder builder); + + /** + * Adds all name/value pairs in the JSON object associated with the specified + * object builder to the JSON object associated with this object builder. + * The newly added name/value pair will replace any existing name/value pair with + * the same name. + * + * @param builder the specified object builder + * @return this object builder + * @throws NullPointerException if the specified builder is null + * @since 1.1 + */ + default JsonObjectBuilder addAll(JsonObjectBuilder builder) { + throw new UnsupportedOperationException(); + } + + /** + * Remove the name/value pair from the JSON object associated with this + * object builder if it is present. + * + * @param name the name in the name/value pair to be removed + * @return this object builder + * @throws NullPointerException if the specified name is null + * @since 1.1 + */ + default JsonObjectBuilder remove(String name) { + throw new UnsupportedOperationException(); + } + + /** + * Returns the JSON object associated with this object builder. + * The iteration order for the {@code JsonObject} is based + * on the order in which name/value pairs are added to the object using + * this builder. + * + * @return JSON object that is being built + */ + JsonObject build(); + +} diff --git a/api/src/main/java/javax/json/JsonPatch.java b/api/src/main/java/javax/json/JsonPatch.java new file mode 100644 index 00000000..0a8948ca --- /dev/null +++ b/api/src/main/java/javax/json/JsonPatch.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json; + +/** + *

This interface represents an immutable implementation of a JSON Patch + * as defined by RFC 6902. + *

+ *

A {@code JsonPatch} can be instantiated with {@link Json#createPatch(JsonArray)} + * by specifying the patch operations in a JSON Patch. Alternately, it + * can also be constructed with a {@link JsonPatchBuilder}. + *

+ * The following illustrates both approaches. + *

1. Construct a JsonPatch with a JSON Patch. + *

{@code
+ *   JsonArray contacts = ... // The target to be patched
+ *   JsonArray patch = ...  ; // JSON Patch
+ *   JsonPatch jsonpatch = Json.createPatch(patch);
+ *   JsonArray result = jsonpatch.apply(contacts);
+ * } 
+ * 2. Construct a JsonPatch with JsonPatchBuilder. + *
{@code
+ *   JsonPatchBuilder builder = Json.createPatchBuilder();
+ *   JsonArray result = builder.add("/John/phones/office", "1234-567")
+ *                             .remove("/Amy/age")
+ *                             .build()
+ *                             .apply(contacts);
+ * } 
+ * + * @see RFC 6902 + * + * @since 1.1 + */ +public interface JsonPatch { + + /** + * This enum represents the list of valid JSON Patch operations + * as defined by RFC 6902. + * + * @see RFC 6902 + */ + enum Operation { + + /** + * "add" operation. + */ + ADD("add"), + + /** + * "remove" operation. + */ + REMOVE("remove"), + + /** + * "remove" operation. + */ + REPLACE("replace"), + + /** + * "move" operation. + */ + MOVE("move"), + + /** + * "copy" operation. + */ + COPY("copy"), + + /** + * "test" operation. + */ + TEST("test"); + + private final String operationName; + + private Operation(String operationName) { + this.operationName = operationName; + } + + /** + * Returns enum constant name as lower case string. + * + * @return lower case name of the enum constant + */ + public String operationName() { + return operationName; + } + + /** + * Returns the enum constant with the specified name. + * + * @param operationName {@code operationName} to convert to the enum constant. + * @return the enum constant for given {@code operationName} + * @throws JsonException if given {@code operationName} is not recognized + */ + public static Operation fromOperationName(String operationName) { + for (Operation op : values()) { + if (op.operationName().equalsIgnoreCase(operationName)) { + return op; + } + } + throw new JsonException("Illegal value for the operationName of the JSON patch operation: " + operationName); + } + } + + /** + * Applies the patch operations to the specified {@code target}. + * The target is not modified by the patch. + * + * @param the target type, must be a subtype of {@link JsonStructure} + * @param target the target to apply the patch operations + * @return the transformed target after the patch + * @throws JsonException if the supplied JSON Patch is malformed or if + * it contains references to non-existing members + */ + T apply(T target); + + /** + * Returns the {@code JsonPatch} as {@code JsonArray}. + * + * @return this {@code JsonPatch} as {@code JsonArray} + */ + JsonArray toJsonArray(); + +} diff --git a/api/src/main/java/javax/json/JsonPatchBuilder.java b/api/src/main/java/javax/json/JsonPatchBuilder.java new file mode 100644 index 00000000..333f781c --- /dev/null +++ b/api/src/main/java/javax/json/JsonPatchBuilder.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json; + +/** + * A builder for constructing a JSON Patch as defined by + * RFC 6902 by adding + * JSON Patch operations incrementally. + *

+ * The following illustrates the approach. + *

+ *   JsonPatchBuilder builder = Json.createPatchBuilder();
+ *   JsonPatch patch = builder.add("/John/phones/office", "1234-567")
+ *                            .remove("/Amy/age")
+ *                            .build();
+ * 
+ * The result is equivalent to the following JSON Patch. + *
+ * [
+ *    {"op" = "add", "path" = "/John/phones/office", "value" = "1234-567"},
+ *    {"op" = "remove", "path" = "/Amy/age"}
+ * ] 
+ * + * @see RFC 6902 + * + * @since 1.1 + */ +public interface JsonPatchBuilder { + + /** + * Adds an "add" JSON Patch operation. + * + * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer. + * @param value the "value" member of the operation + * @return this JsonPatchBuilder + */ + JsonPatchBuilder add(String path, JsonValue value); + + /** + * Adds an "add" JSON Patch operation. + * + * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer. + * @param value the "value" member of the operation + * @return this JsonPatchBuilder + */ + JsonPatchBuilder add(String path, String value); + + /** + * Adds an "add" JSON Patch operation. + * + * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer. + * @param value the "value" member of the operation + * @return this JsonPatchBuilder + */ + JsonPatchBuilder add(String path, int value); + + /** + * Adds an "add" JSON Patch operation. + * + * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer. + * @param value the "value" member of the operation + * @return this JsonPatchBuilder + */ + JsonPatchBuilder add(String path, boolean value); + + /** + * Adds a "remove" JSON Patch operation. + * + * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer. + * @return this JsonPatchBuilder + */ + JsonPatchBuilder remove(String path); + + /** + * Adds a "replace" JSON Patch operation. + * + * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer. + * @param value the "value" member of the operation + * @return this JsonPatchBuilder + */ + JsonPatchBuilder replace(String path, JsonValue value); + + /** + * Adds a "replace" JSON Patch operation. + * + * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer string. + * @param value the "value" member of the operation + * @return this JsonPatchBuilder + */ + JsonPatchBuilder replace(String path, String value); + + /** + * Adds a "replace" JSON Patch operation. + * + * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer string. + * @param value the "value" member of the operation + * @return this JsonPatchBuilder + */ + JsonPatchBuilder replace(String path, int value); + + /** + * Adds a "replace" JSON Patch operation. + * + * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer string. + * @param value the "value" member of the operation + * @return this JsonPatchBuilder + */ + JsonPatchBuilder replace(String path, boolean value); + + /** + * Adds a "move" JSON Patch operation. + * + * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer string. + * @param from the "from" member of the operation + * @return this JsonPatchBuilder + */ + JsonPatchBuilder move(String path, String from); + + /** + * Adds a "copy" JSON Patch operation. + * + * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer string. + * @param from the "from" member of the operation + * @return this JsonPatchBuilder + */ + JsonPatchBuilder copy(String path, String from); + + /** + * Adds a "test" JSON Patch operation. + * + * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer string. + * @param value the "value" member of the operation + * @return this JsonPatchBuilder + */ + JsonPatchBuilder test(String path, JsonValue value); + + /** + * Adds a "test" JSON Patch operation. + * + * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer string. + * @param value the "value" member of the operation + * @return this JsonPatchBuilder + */ + JsonPatchBuilder test(String path, String value); + + /** + * Adds a "test" JSON Patch operation. + * + * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer string. + * @param value the "value" member of the operation + * @return this JsonPatchBuilder + */ + JsonPatchBuilder test(String path, int value); + + /** + * Adds a "test" JSON Patch operation. + * + * @param path the "path" member of the operation. Must be a valid escaped JSON-Pointer string. + * @param value the "value" member of the operation + * @return this JsonPatchBuilder + */ + JsonPatchBuilder test(String path, boolean value); + + + /** + * Returns the JSON Patch. + * + * @return a JSON Patch + */ + JsonPatch build(); + +} diff --git a/api/src/main/java/javax/json/JsonPointer.java b/api/src/main/java/javax/json/JsonPointer.java new file mode 100644 index 00000000..c7e30023 --- /dev/null +++ b/api/src/main/java/javax/json/JsonPointer.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json; + +/** + *

This interface represents an immutable implementation of a JSON Pointer + * as defined by RFC 6901. + *

+ *

A JSON Pointer, when applied to a target {@link JsonValue}, + * defines a reference location in the target.

+ *

An empty JSON Pointer string defines a reference to the target itself.

+ *

If the JSON Pointer string is non-empty, it must be a sequence + * of '/' prefixed tokens, and the target must either be a {@link JsonArray} + * or {@link JsonObject}. If the target is a {@code JsonArray}, the pointer + * defines a reference to an array element, and the last token specifies the index. + * If the target is a {@link JsonObject}, the pointer defines a reference to a + * name/value pair, and the last token specifies the name. + *

+ *

The method {@link #getValue getValue()} returns the referenced value. + * Methods {@link #add add()}, {@link #replace replace()}, + * and {@link #remove remove()} execute operations specified in + * RFC 6902.

+ * + * @see RFC 6901 + * @see RFC 6902 + * + * @since 1.1 + */ +public interface JsonPointer { + + /** + * Adds or replaces a value at the referenced location in the specified + * {@code target} with the specified {@code value}. + *
    + *
  1. If the reference is the target (empty JSON Pointer string), + * the specified {@code value}, which must be the same type as + * specified {@code target}, is returned.
  2. + *
  3. If the reference is an array element, the specified {@code value} is inserted + * into the array, at the referenced index. The value currently at that location, and + * any subsequent values, are shifted to the right (adds one to the indices). + * Index starts with 0. If the reference is specified with a "-", or if the + * index is equal to the size of the array, the value is appended to the array.
  4. + *
  5. If the reference is a name/value pair of a {@code JsonObject}, and the + * referenced value exists, the value is replaced by the specified {@code value}. + * If the value does not exist, a new name/value pair is added to the object.
  6. + *
+ * + * @param the target type, must be a subtype of {@link JsonValue} + * @param target the target referenced by this {@code JsonPointer} + * @param value the value to be added + * @return the transformed {@code target} after the value is added. + * @throws NullPointerException if {@code target} is {@code null} + * @throws JsonException if the reference is an array element and + * the index is out of range ({@code index < 0 || index > array size}), + * or if the pointer contains references to non-existing objects or arrays. + */ + T add(T target, JsonValue value); + + /** + * Removes the value at the reference location in the specified {@code target}. + * + * @param the target type, must be a subtype of {@link JsonValue} + * @param target the target referenced by this {@code JsonPointer} + * @return the transformed {@code target} after the value is removed. + * @throws NullPointerException if {@code target} is {@code null} + * @throws JsonException if the referenced value does not exist, + * or if the reference is the target. + */ + T remove(T target); + + /** + * Replaces the value at the referenced location in the specified + * {@code target} with the specified {@code value}. + * + * @param the target type, must be a subtype of {@link JsonValue} + * @param target the target referenced by this {@code JsonPointer} + * @param value the value to be stored at the referenced location + * @return the transformed {@code target} after the value is replaced. + * @throws NullPointerException if {@code target} is {@code null} + * @throws JsonException if the referenced value does not exist, + * or if the reference is the target. + */ + T replace(T target, JsonValue value); + + /** + * Returns {@code true} if there is a value at the referenced location in the specified {@code target}. + * + * @param target the target referenced by this {@code JsonPointer} + * @return {@code true} if this pointer points to a value in a specified {@code target}. + */ + boolean containsValue(JsonStructure target); + + /** + * Returns the value at the referenced location in the specified {@code target}. + * + * @param target the target referenced by this {@code JsonPointer} + * @return the referenced value in the target. + * @throws NullPointerException if {@code target} is null + * @throws JsonException if the referenced value does not exist + */ + JsonValue getValue(JsonStructure target); +} diff --git a/api/src/main/java/javax/json/JsonReader.java b/api/src/main/java/javax/json/JsonReader.java new file mode 100644 index 00000000..99c12406 --- /dev/null +++ b/api/src/main/java/javax/json/JsonReader.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json; + +import java.io.Closeable; + +/** + * Reads a JSON {@link JsonObject object} or an {@link JsonArray array} + * structure from an input source. + * + *

The class {@link javax.json.Json} contains methods to create readers from + * input sources ({@link java.io.InputStream} and {@link java.io.Reader}). + * + *

+ * The following example demonstrates how to read an empty JSON array from + * a string: + *

+ * 
+ * JsonReader jsonReader = Json.createReader(new StringReader("[]"));
+ * JsonArray array = jsonReader.readArray();
+ * jsonReader.close();
+ * 
+ * 
+ * + *

+ * The class {@link JsonReaderFactory} also contains methods to create + * {@code JsonReader} instances. A factory instance can be used to create + * multiple reader instances with the same configuration. This the preferred + * way to create multiple instances. A sample usage is shown in the following + * example: + *

+ * 
+ * JsonReaderFactory factory = Json.createReaderFactory(config);
+ * JsonReader reader1 = factory.createReader(...);
+ * JsonReader reader2 = factory.createReader(...);
+ * 
+ * 
+ */ +public interface JsonReader extends /*Auto*/Closeable { + + /** + * Returns a JSON array or object that is represented in + * the input source. This method needs to be called + * only once for a reader instance. + * + * @return a JSON object or array + * @throws JsonException if a JSON object or array cannot + * be created due to i/o error (IOException would be + * cause of JsonException) + * @throws javax.json.stream.JsonParsingException if a JSON object or array + * cannot be created due to incorrect representation + * @throws IllegalStateException if read, readObject, readArray, + * readValue or close method is already called + */ + JsonStructure read(); + + /** + * Returns a JSON object that is represented in + * the input source. This method needs to be called + * only once for a reader instance. + * + * @return a JSON object + * @throws JsonException if a JSON object cannot + * be created due to i/o error (IOException would be + * cause of JsonException) + * @throws javax.json.stream.JsonParsingException if a JSON object cannot + * be created due to incorrect representation + * @throws IllegalStateException if read, readObject, readArray, + * readValue or close method is already called + */ + JsonObject readObject(); + + /** + * Returns a JSON array that is represented in + * the input source. This method needs to be called + * only once for a reader instance. + * + * @return a JSON array + * @throws JsonException if a JSON array cannot + * be created due to i/o error (IOException would be + * cause of JsonException) + * @throws javax.json.stream.JsonParsingException if a JSON array cannot + * be created due to incorrect representation + * @throws IllegalStateException if read, readObject, readArray, + * readValue or close method is already called + */ + JsonArray readArray(); + + /** + * Returns a JSON value that is represented in + * the input source. This method needs to be called + * only once for a reader instance. + * + * @return a JSON value + * @throws JsonException if a JSON value + * be created due to i/o error (IOException would be + * cause of JsonException) + * @throws javax.json.stream.JsonParsingException if a JSON value + * cannot be created due to incorrect representation + * @throws IllegalStateException if read, readObject, readArray, + * readValue or close method is already called + * + * @since 1.1 + */ + default JsonValue readValue() { + throw new UnsupportedOperationException(); + } + + /** + * Closes this reader and frees any resources associated with the + * reader. This method closes the underlying input source. + * + * @throws JsonException if an i/o error occurs (IOException would be + * cause of JsonException) + */ + @Override + void close(); + +} diff --git a/api/src/main/java/javax/json/JsonReaderFactory.java b/api/src/main/java/javax/json/JsonReaderFactory.java new file mode 100644 index 00000000..4a4f0fb9 --- /dev/null +++ b/api/src/main/java/javax/json/JsonReaderFactory.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json; + +import java.io.InputStream; +import java.io.Reader; +import java.nio.charset.Charset; +import java.util.Map; + +/** + * Factory to create {@link javax.json.JsonReader} instances. If a factory + * instance is configured with some configuration, that would be + * used to configure the created reader instances. + * + *

+ * {@link javax.json.JsonReader} can also be created using {@link Json}'s + * {@code createReader} methods. If multiple reader instances are created, + * then creating them using a reader factory is preferred. + * + *

+ * For example: + *

+ * 
+ * JsonReaderFactory factory = Json.createReaderFactory(...);
+ * JsonReader reader1 = factory.createReader(...);
+ * JsonReader reader2 = factory.createReader(...);
+ * 
+ * 
+ * + *

All the methods in this class are safe for use by multiple concurrent + * threads. + */ +public interface JsonReaderFactory { + + /** + * Creates a JSON reader from a character stream. The reader is configured + * with the factory configuration. + * + * @param reader a reader from which JSON is to be read + * @return a JSON reader + */ + JsonReader createReader(Reader reader); + + /** + * Creates a JSON reader from a byte stream. The character encoding of + * the stream is determined as described in + * RFC 7159. + * The reader is configured with the factory configuration. + * + * @param in a byte stream from which JSON is to be read + * @return a JSON reader + */ + JsonReader createReader(InputStream in); + + /** + * Creates a JSON reader from a byte stream. The bytes of the stream + * are decoded to characters using the specified charset. The reader is + * configured with the factory configuration. + * + * @param in a byte stream from which JSON is to be read + * @param charset a charset + * @return a JSON reader + */ + JsonReader createReader(InputStream in, Charset charset); + + /** + * Returns read-only map of supported provider specific configuration + * properties that are used to configure the created JSON readers. + * If there are any specified configuration properties that are not + * supported by the provider, they won't be part of the returned map. + * + * @return a map of supported provider specific properties that are used + * to configure the readers. The map be empty but not null. + */ + Map getConfigInUse(); + +} diff --git a/api/src/main/java/javax/json/JsonString.java b/api/src/main/java/javax/json/JsonString.java new file mode 100644 index 00000000..6f341c4b --- /dev/null +++ b/api/src/main/java/javax/json/JsonString.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2011, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json; + +/** + * An immutable JSON string value. + */ +public interface JsonString extends JsonValue { + + /** + * Returns the JSON string value. + * + * @return a JSON string value + */ + String getString(); + + + /** + * Returns the char sequence for the JSON String value + * + * @return a char sequence for the JSON String value + */ + CharSequence getChars(); + + /** + * Compares the specified object with this {@code JsonString} for equality. + * Returns {@code true} if and only if the specified object is also a + * {@code JsonString}, and their {@link #getString()} objects are + * equal. + * + * @param obj the object to be compared for equality with this + * {@code JsonString} + * @return {@code true} if the specified object is equal to this + * {@code JsonString} + */ + @Override + boolean equals(Object obj); + + /** + * Returns the hash code value for this {@code JsonString} object. + * The hash code of a {@code JsonString} object is defined to be its + * {@link #getString()} object's hash code. + * + * @return the hash code value for this {@code JsonString} object + */ + @Override + int hashCode(); + +} diff --git a/api/src/main/java/javax/json/JsonStructure.java b/api/src/main/java/javax/json/JsonStructure.java new file mode 100644 index 00000000..6dc9a771 --- /dev/null +++ b/api/src/main/java/javax/json/JsonStructure.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json; + +/** + * Super type for the two structured types in JSON ({@link JsonObject object}s + * and {@link JsonArray array}s). + */ +public interface JsonStructure extends JsonValue { + + /** + * Get the value referenced by the provided JSON Pointer in the JsonStructure. + * + * @param jsonPointer the JSON Pointer + * @return the {@code JsonValue} at the referenced location + * @throws JsonException if the JSON Pointer is malformed, or if it references + * a non-existing member or value. + * + * @since 1.1 + */ + default public JsonValue getValue(String jsonPointer) { + return Json.createPointer(jsonPointer).getValue(this); + } +} diff --git a/api/src/main/java/javax/json/JsonValue.java b/api/src/main/java/javax/json/JsonValue.java new file mode 100644 index 00000000..c4968e06 --- /dev/null +++ b/api/src/main/java/javax/json/JsonValue.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2011, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json; + +/** + * JsonValue represents an immutable JSON value. + * + * + *

A JSON value is one of the following: + * an object ({@link JsonObject}), an array ({@link JsonArray}), + * a number ({@link JsonNumber}), a string ({@link JsonString}), + * {@code true} ({@link JsonValue#TRUE JsonValue.TRUE}), {@code false} + * ({@link JsonValue#FALSE JsonValue.FALSE}), + * or {@code null} ({@link JsonValue#NULL JsonValue.NULL}). + */ +public interface JsonValue { + + /** + * The empty JSON object. + * + * @since 1.1 + */ + static final JsonObject EMPTY_JSON_OBJECT = new EmptyObject(); + + /** + * The empty JSON array. + * + * @since 1.1 + */ + static final JsonArray EMPTY_JSON_ARRAY = new EmptyArray(); + + /** + * Indicates the type of a {@link JsonValue} object. + */ + enum ValueType { + /** + * JSON array. + */ + ARRAY, + + /** + * JSON object. + */ + OBJECT, + + /** + * JSON string. + */ + STRING, + + /** + * JSON number. + */ + NUMBER, + + /** + * JSON true. + */ + TRUE, + + /** + * JSON false. + */ + FALSE, + + /** + * JSON null. + */ + NULL + } + + /** + * JSON null value. + */ + static final JsonValue NULL = new JsonValueImpl(ValueType.NULL); + + /** + * JSON true value. + */ + static final JsonValue TRUE = new JsonValueImpl(ValueType.TRUE); + + /** + * JSON false value. + */ + static final JsonValue FALSE = new JsonValueImpl(ValueType.FALSE); + + /** + * Returns the value type of this JSON value. + * + * @return JSON value type + */ + ValueType getValueType(); + + /** + * Return the JsonValue as a JsonObject + * + * @return the JsonValue as a JsonObject + * @throws ClassCastException if the JsonValue is not a JsonObject + * + * @since 1.1 + */ + default JsonObject asJsonObject() { + return JsonObject.class.cast(this); + } + + /** + * Return the JsonValue as a JsonArray + * + * @return the JsonValue as a JsonArray + * @throws ClassCastException if the JsonValue is not a JsonArray + * + * @since 1.1 + */ + default JsonArray asJsonArray() { + return JsonArray.class.cast(this); + } + + /** + * Returns JSON text for this JSON value. + * + * @return JSON text + */ + @Override + String toString(); + +} diff --git a/api/src/main/java/javax/json/JsonValueImpl.java b/api/src/main/java/javax/json/JsonValueImpl.java new file mode 100644 index 00000000..1a4a2e40 --- /dev/null +++ b/api/src/main/java/javax/json/JsonValueImpl.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2016, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json; + +import java.io.Serializable; + +/** + * Private implementation of {@link JsonValue} for simple {@link ValueType}s + * allowing their usage in constants which are better to implement {@link Serializable}. + * + * @author Lukas Jungmann + */ +final class JsonValueImpl implements JsonValue, Serializable { + + private final ValueType valueType; + + JsonValueImpl(ValueType valueType) { + this.valueType = valueType; + } + + /** + * Returns the value type of this JSON value. + * + * @return JSON value type + */ + @Override + public ValueType getValueType() { + return valueType; + } + + /** + * Compares the specified object with this {@link JsonValue} + * object for equality. Returns {@code true} if and only if the + * specified object is also a JsonValue, and their + * {@link #getValueType()} objects are equal. + * + * @param obj the object to be compared for equality with this JsonValue + * @return {@code true} if the specified object is equal to this + * JsonValue + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof JsonValue) { + return getValueType().equals(((JsonValue) obj).getValueType()); + } + return false; + } + + /** + * Returns the hash code value for this {@link JsonValue} object. + * The hash code of the {@link JsonValue} object is defined to be + * its {@link #getValueType()} object's hash code. + * + * @return the hash code value for this {@link JsonValue} object + */ + @Override + public int hashCode() { + return valueType.hashCode(); + } + + @Override + public String toString() { + return valueType.name().toLowerCase(); + } + +} diff --git a/api/src/main/java/javax/json/JsonWriter.java b/api/src/main/java/javax/json/JsonWriter.java new file mode 100644 index 00000000..16bfd6f8 --- /dev/null +++ b/api/src/main/java/javax/json/JsonWriter.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json; + +import java.io.Closeable; + +/** + * Writes a JSON {@link JsonObject object} or {@link JsonArray array} structure + * to an output source. + * + *

The class {@link javax.json.Json} contains methods to create writers from + * output sources ({@link java.io.OutputStream} and {@link java.io.Writer}). + * + *

+ * The following example demonstrates how write an empty JSON object: + *

+ * 
+ * JsonWriter jsonWriter = Json.createWriter(...);
+ * jsonWriter.writeObject(Json.createObjectBuilder().build());
+ * jsonWriter.close();
+ * 
+ * 
+ * + *

+ * The class {@link JsonWriterFactory} also contains methods to create + * {@code JsonWriter} instances. A factory instance can be used to create + * multiple writer instances with the same configuration. This the preferred + * way to create multiple instances. A sample usage is shown in the following + * example: + *

+ * 
+ * JsonWriterFactory factory = Json.createWriterFactory(config);
+ * JsonWriter writer1 = factory.createWriter(...);
+ * JsonWriter writer2 = factory.createWriter(...);
+ * 
+ * 
+ */ +public interface JsonWriter extends /*Auto*/Closeable { + + /** + * Writes the specified JSON {@link JsonArray array} to the output + * source. This method needs to be called only once for a writer instance. + * + * @param array JSON array that is to be written to the output source + * @throws JsonException if the specified JSON object cannot be + * written due to i/o error (IOException would be cause of + * JsonException) + * @throws IllegalStateException if writeArray, writeObject, write or close + * method is already called + */ + void writeArray(JsonArray array); + + /** + * Writes the specified JSON {@link JsonObject object} to the output + * source. This method needs to be called only once for a writer instance. + * + * @param object JSON object that is to be written to the output source + * @throws JsonException if the specified JSON object cannot be + * written due to i/o error (IOException would be cause of JsonException) + * @throws IllegalStateException if writeArray, writeObject, write or close + * method is already called + */ + void writeObject(JsonObject object); + + /** + * Writes the specified JSON {@link JsonObject object} or + * {@link JsonArray array} to the output source. This method needs + * to be called only once for a writer instance. + * + * @param value JSON array or object that is to be written to the output + * source + * @throws JsonException if the specified JSON object cannot be + * written due to i/o error (IOException would be cause of + * JsonException) + * @throws IllegalStateException if writeArray, writeObject, write + * or close method is already called + */ + void write(JsonStructure value); + + /** + * Closes this JSON writer and frees any resources associated with the + * writer. This method closes the underlying output source. + * + * @throws JsonException if an i/o error occurs (IOException would be + * cause of JsonException) + */ + + /** + * Writes the specified {@link JsonValue} to the output source. + * method needs to be called only once for a write instance. + * + * @param value a {@code JsonValue} to be written to the output + * source + * @throws JsonException if the specified JSON object cannot be + * written due to i/o error (IOException would be cause of + * JsonException) + * @throws IllegalStateException if writeArray, writeObject, write + * or close method is already called + * + * @since 1.1 + */ + default void write(JsonValue value) { + throw new UnsupportedOperationException(); + } + + @Override + void close(); + +} diff --git a/api/src/main/java/javax/json/JsonWriterFactory.java b/api/src/main/java/javax/json/JsonWriterFactory.java new file mode 100644 index 00000000..540721eb --- /dev/null +++ b/api/src/main/java/javax/json/JsonWriterFactory.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json; + +import java.io.OutputStream; +import java.io.Writer; +import java.nio.charset.Charset; +import java.util.Map; + +/** + * Factory to create {@link javax.json.JsonWriter} instances. If a factory + * instance is configured with some configuration, that would be + * used to configure the created writer instances. + * + *

+ * {@link javax.json.JsonWriter} can also be created using {@link Json}'s + * {@code createWriter} methods. If multiple writer instances are created, + * then creating them using a writer factory is preferred. + * + *

+ * For example: + *

+ * 
+ * JsonWriterFactory factory = Json.createWriterFactory(...);
+ * JsonWriter writer1 = factory.createWriter(...);
+ * JsonWriter writer2 = factory.createWriter(...);
+ * 
+ * 
+ * + *

All the methods in this class are safe for use by multiple concurrent + * threads. + */ +public interface JsonWriterFactory { + + /** + * Creates a JSON writer to write a JSON {@link JsonObject object} or + * {@link JsonArray array} structure to the specified character stream. + * The writer is configured with the factory configuration. + * + * @param writer to which JSON object or array is written + * @return a JSON writer + */ + JsonWriter createWriter(Writer writer); + + /** + * Creates a JSON writer to write a JSON {@link JsonObject object} or + * {@link JsonArray array} structure to the specified byte stream. + * Characters written to the stream are encoded into bytes using UTF-8 + * encoding. The writer is configured with the factory configuration. + * + * @param out to which JSON object or array is written + * @return a JSON writer + */ + JsonWriter createWriter(OutputStream out); + + /** + * Creates a JSON writer to write a JSON {@link JsonObject object} or + * {@link JsonArray array} structure to the specified byte stream. + * Characters written to the stream are encoded into bytes using the + * specified charset. The writer is configured with the factory + * configuration. + * + * @param out to which JSON object or array is written + * @param charset a charset + * @return a JSON writer + */ + JsonWriter createWriter(OutputStream out, Charset charset); + + /** + * Returns read-only map of supported provider specific configuration + * properties that are used to configure the created JSON writer objects. + * If there are any specified configuration properties that are not + * supported by the provider, they won't be part of the returned map. + * + * @return a map of supported provider specific properties that are used + * to configure the created writers. The map may be empty but not null. + */ + Map getConfigInUse(); + +} diff --git a/api/src/main/java/javax/json/package-info.java b/api/src/main/java/javax/json/package-info.java new file mode 100644 index 00000000..e79212c9 --- /dev/null +++ b/api/src/main/java/javax/json/package-info.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2011, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +/** + * Provides an object model API to process JSON. + * + *

The object model API is a high-level API that provides immutable object + * models for JSON object and array structures. These JSON structures are + * represented as object models using the Java types {@link javax.json.JsonObject} + * and {@link javax.json.JsonArray}. The interface {@code javax.json.JsonObject} provides + * a {@link java.util.Map} view to access the unordered collection of zero or + * more name/value pairs from the model. Similarly, the interface + * {@code JsonArray} provides a {@link java.util.List} view to access the + * ordered sequence of zero or more values from the model. + * + *

The object model API uses builder patterns to create and modify + * these object models. The classes {@link javax.json.JsonObjectBuilder} and + * {@link javax.json.JsonArrayBuilder} provide methods to create and modify models + * of type {@code JsonObject} and {@code JsonArray} respectively. + * + *

These object models can also be created from an input source using + * the class {@link javax.json.JsonReader}. Similarly, these object models + * can be written to an output source using the class {@link javax.json.JsonWriter}. + *

+ * This package includes several classes that implement other JSON related + * standards: JSON Pointer, + * JSON Patch, and + * JSON Merge Patch. + * They can be used to retrieve, transform or manipulate values in an + * object model. + */ +package javax.json; diff --git a/api/src/main/java/javax/json/spi/JsonProvider.java b/api/src/main/java/javax/json/spi/JsonProvider.java new file mode 100644 index 00000000..f1ac7c8d --- /dev/null +++ b/api/src/main/java/javax/json/spi/JsonProvider.java @@ -0,0 +1,480 @@ +/* + * Copyright (c) 2011, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json.spi; + +import javax.json.*; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonGeneratorFactory; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParserFactory; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.ServiceLoader; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Optional; + +/** + * Service provider for JSON processing objects. + * + *

All the methods in this class are safe for use by multiple concurrent + * threads. + * + * @see ServiceLoader + */ +public abstract class JsonProvider { + + /** + * A constant representing the name of the default + * {@code JsonProvider} implementation class. + */ + private static final String DEFAULT_PROVIDER + = "org.glassfish.json.JsonProviderImpl"; + + protected JsonProvider() { + } + + /** + * Creates a JSON provider object. The provider is loaded using the + * {@link ServiceLoader#load(Class)} method. If there are no available + * service providers, this method returns the default service provider. + * Users are recommended to cache the result of this method. + * + * @see ServiceLoader + * @return a JSON provider + */ + public static JsonProvider provider() { + ServiceLoader loader = ServiceLoader.load(JsonProvider.class); + Iterator it = loader.iterator(); + if (it.hasNext()) { + return it.next(); + } + try { + Class clazz = Class.forName(DEFAULT_PROVIDER); + return (JsonProvider) clazz.newInstance(); + } catch (ClassNotFoundException x) { + throw new JsonException( + "Provider " + DEFAULT_PROVIDER + " not found", x); + } catch (Exception x) { + throw new JsonException( + "Provider " + DEFAULT_PROVIDER + " could not be instantiated: " + x, + x); + } + } + + /** + * Creates a JSON parser from a character stream. + * + * @param reader i/o reader from which JSON is to be read + * @return a JSON parser + */ + public abstract JsonParser createParser(Reader reader); + + /** + * Creates a JSON parser from the specified byte stream. + * The character encoding of the stream is determined + * as defined in RFC 7159 + * . + * + * @param in i/o stream from which JSON is to be read + * @throws JsonException if encoding cannot be determined + * or i/o error (IOException would be cause of JsonException) + * @return a JSON parser + */ + public abstract JsonParser createParser(InputStream in); + + /** + * Creates a parser factory for creating {@link JsonParser} instances. + * + * @return a JSON parser factory + * + public abstract JsonParserFactory createParserFactory(); + */ + + /** + * Creates a parser factory for creating {@link JsonParser} instances. + * The factory is configured with the specified map of + * provider specific configuration properties. Provider implementations + * should ignore any unsupported configuration properties specified in + * the map. + * + * @param config a map of provider specific properties to configure the + * JSON parsers. The map may be empty or null + * @return a JSON parser factory + */ + public abstract JsonParserFactory createParserFactory(Map config); + + /** + * Creates a JSON generator for writing JSON text to a character stream. + * + * @param writer a i/o writer to which JSON is written + * @return a JSON generator + */ + public abstract JsonGenerator createGenerator(Writer writer); + + /** + * Creates a JSON generator for writing JSON text to a byte stream. + * + * @param out i/o stream to which JSON is written + * @return a JSON generator + */ + public abstract JsonGenerator createGenerator(OutputStream out); + + /** + * Creates a generator factory for creating {@link JsonGenerator} instances. + * + * @return a JSON generator factory + * + public abstract JsonGeneratorFactory createGeneratorFactory(); + */ + + /** + * Creates a generator factory for creating {@link JsonGenerator} instances. + * The factory is configured with the specified map of provider specific + * configuration properties. Provider implementations should + * ignore any unsupported configuration properties specified in the map. + * + * @param config a map of provider specific properties to configure the + * JSON generators. The map may be empty or null + * @return a JSON generator factory + */ + public abstract JsonGeneratorFactory createGeneratorFactory(Map config); + + /** + * Creates a JSON reader from a character stream. + * + * @param reader a reader from which JSON is to be read + * @return a JSON reader + */ + public abstract JsonReader createReader(Reader reader); + + /** + * Creates a JSON reader from a byte stream. The character encoding of + * the stream is determined as described in + * RFC 7159. + * + * @param in a byte stream from which JSON is to be read + * @return a JSON reader + */ + public abstract JsonReader createReader(InputStream in); + + /** + * Creates a JSON writer to write a + * JSON {@link JsonObject object} or {@link JsonArray array} + * structure to the specified character stream. + * + * @param writer to which JSON object or array is written + * @return a JSON writer + */ + public abstract JsonWriter createWriter(Writer writer); + + /** + * Creates a JSON writer to write a + * JSON {@link JsonObject object} or {@link JsonArray array} + * structure to the specified byte stream. Characters written to + * the stream are encoded into bytes using UTF-8 encoding. + * + * @param out to which JSON object or array is written + * @return a JSON writer + */ + public abstract JsonWriter createWriter(OutputStream out); + + /** + * Creates a writer factory for creating {@link JsonWriter} objects. + * The factory is configured with the specified map of provider specific + * configuration properties. Provider implementations should ignore any + * unsupported configuration properties specified in the map. + * + * @param config a map of provider specific properties to configure the + * JSON writers. The map may be empty or null + * @return a JSON writer factory + */ + public abstract JsonWriterFactory createWriterFactory(Map config); + + /** + * Creates a reader factory for creating {@link JsonReader} objects. + * The factory is configured with the specified map of provider specific + * configuration properties. Provider implementations should ignore any + * unsupported configuration properties specified in the map. + * + * @param config a map of provider specific properties to configure the + * JSON readers. The map may be empty or null + * @return a JSON reader factory + */ + public abstract JsonReaderFactory createReaderFactory(Map config); + + /** + * Creates a JSON object builder. + * + * @return a JSON object builder + */ + public abstract JsonObjectBuilder createObjectBuilder(); + + /** + * Creates a JSON object builder, initialized with the specified object. + * + * @param object the initial JSON object in the builder + * @return a JSON object builder + * + * @since 1.1 + */ + public JsonObjectBuilder createObjectBuilder(JsonObject object) { + throw new UnsupportedOperationException(); + } + + /** + * Creates a JSON object builder, initialized with the data from specified {@code map}. + * If the @{code map} contains {@link Optional}s then resulting JSON object builder + * contains the key from the {@code map} only if the {@link Optional} is not empty. + * + * @param map the initial object in the builder + * @return a JSON object builder + * @exception IllegalArgumentException if the value from the {@code map} cannot be converted + * to the corresponding {@link JsonValue} + * + * @since 1.1 + */ + public JsonObjectBuilder createObjectBuilder(Map map) { + throw new UnsupportedOperationException(); + } + + /** + * Creates a JSON array builder. + * + * @return a JSON array builder + */ + public abstract JsonArrayBuilder createArrayBuilder(); + + /** + * Creates a JSON array builder, initialized with the specified array. + * + * @param array the initial JSON array in the builder + * @return a JSON array builder + * + * @since 1.1 + */ + public JsonArrayBuilder createArrayBuilder(JsonArray array) { + throw new UnsupportedOperationException(); + } + + /** + * Creates JSON Pointer (RFC 6901) + * from given {@code jsonPointer} string. + *

    + *
  • An empty {@code jsonPointer} string defines a reference to the target itself.
  • + *
  • If the {@code jsonPointer} string is non-empty, it must be a sequence of '{@code /}' prefixed tokens.
  • + *
+ * + * @param jsonPointer the JSON Pointer string + * @throws NullPointerException if {@code jsonPointer} is {@code null} + * @throws JsonException if {@code jsonPointer} is not a valid JSON Pointer + * @return a JSON Pointer + * + * @since 1.1 + */ + public JsonPointer createPointer(String jsonPointer) { + throw new UnsupportedOperationException(); + } + + /** + * Creates a JSON Patch builder (RFC 6902). + * + * @return a JSON Patch builder + * + * @since 1.1 + */ + public JsonPatchBuilder createPatchBuilder() { + throw new UnsupportedOperationException(); + } + + /** + * Creates a JSON Patch builder + * (RFC 6902), + * initialized with the specified operations. + * + * @param array the initial patch operations + * @return a JSON Patch builder + * + * @since 1.1 + */ + public JsonPatchBuilder createPatchBuilder(JsonArray array) { + throw new UnsupportedOperationException(); + } + + /** + * Creates a JSON Patch (RFC 6902) + * from the specified operations. + * + * @param array patch operations + * @return a JSON Patch + * + * @since 1.1 + */ + public JsonPatch createPatch(JsonArray array) { + throw new UnsupportedOperationException(); + } + + /** + * Generates a JSON Patch (RFC 6902) + * from the source and target {@code JsonStructure}. + * The generated JSON Patch need not be unique. + * + * @param source the source + * @param target the target, must be the same type as the source + * @return a JSON Patch which when applied to the source, yields the target + * + * @since 1.1 + */ + public JsonPatch createDiff(JsonStructure source, JsonStructure target) { + throw new UnsupportedOperationException(); + } + + /** + * Creates JSON Merge Patch (RFC 7396) + * from specified {@code JsonValue}. + * + * @param patch the patch + * @return a JSON Merge Patch + * + * @since 1.1 + */ + public JsonMergePatch createMergePatch(JsonValue patch) { + throw new UnsupportedOperationException(); + } + + /** + * Generates a JSON Merge Patch (RFC 7396) + * from the source and target {@code JsonValue}s + * which when applied to the {@code source}, yields the {@code target}. + * + * @param source the source + * @param target the target + * @return a JSON Merge Patch + * + * @since 1.1 + */ + public JsonMergePatch createMergeDiff(JsonValue source, JsonValue target) { + throw new UnsupportedOperationException(); + } + + /** + * Creates a JSON array builder, initialized with the content of specified {@code collection}. + * If the @{code collection} contains {@link Optional}s then resulting JSON array builder + * contains the value from the {@code collection} only if the {@link Optional} is not empty. + * + * @param collection the initial data for the builder + * @return a JSON array builder + * @exception IllegalArgumentException if the value from the {@code collection} cannot be converted + * to the corresponding {@link JsonValue} + * + * @since 1.1 + */ + public JsonArrayBuilder createArrayBuilder(Collection collection) { + throw new UnsupportedOperationException(); + } + + + /** + * Creates a builder factory for creating {@link JsonArrayBuilder} + * and {@link JsonObjectBuilder} objects. + * The factory is configured with the specified map of provider specific + * configuration properties. Provider implementations should ignore any + * unsupported configuration properties specified in the map. + * + * @param config a map of provider specific properties to configure the + * JSON builders. The map may be empty or null + * @return a JSON builder factory + */ + public abstract JsonBuilderFactory createBuilderFactory(Map config); + + /** + * Creates a JsonString. + * + * @param value a JSON string + * @return the JsonString for the string + * + * @since 1.1 + */ + public JsonString createValue(String value) { + throw new UnsupportedOperationException(); + } + + /** + * Creates a JsonNumber. + * + * @param value a JSON number + * @return the JsonNumber for the number + * + * @since 1.1 + */ + public JsonNumber createValue(int value) { + throw new UnsupportedOperationException(); + } + + /** + * Creates a JsonNumber. + * + * @param value a JSON number + * @return the JsonNumber for the number + * + * @since 1.1 + */ + public JsonNumber createValue(long value) { + throw new UnsupportedOperationException(); + } + + /** + * Creates a JsonNumber. + * + * @param value a JSON number + * @return the JsonNumber for the number + * + * @since 1.1 + */ + public JsonNumber createValue(double value) { + throw new UnsupportedOperationException(); + } + + /** + * Creates a JsonNumber. + * + * @param value a JSON number + * @return the JsonNumber for the number + * + * @since 1.1 + */ + public JsonNumber createValue(BigDecimal value) { + throw new UnsupportedOperationException(); + } + + /** + * Creates a JsonNumber. + * + * @param value a JSON number + * @return the JsonNumber for the number + * + * @since 1.1 + */ + public JsonNumber createValue(BigInteger value) { + throw new UnsupportedOperationException(); + } +} diff --git a/api/src/main/java/javax/json/spi/package-info.java b/api/src/main/java/javax/json/spi/package-info.java new file mode 100644 index 00000000..83e41e05 --- /dev/null +++ b/api/src/main/java/javax/json/spi/package-info.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +/** + * Service Provider Interface (SPI) to plug in implementations for + * JSON processing objects. + * + *

{@link javax.json.spi.JsonProvider JsonProvider} is an abstract class + * that provides a service for creating JSON processing instances. + * A service provider for {@code JsonProvider} provides an + * specific implementation by subclassing and implementing the methods in + * {@code JsonProvider}. This enables using custom, efficient JSON processing + * implementations (for e.g. parser and generator) other than the default ones. + * + *

The API locates and loads providers using {@link java.util.ServiceLoader}. + * + * @since JSON Processing 1.0 + */ +package javax.json.spi; diff --git a/api/src/main/java/javax/json/stream/JsonCollectors.java b/api/src/main/java/javax/json/stream/JsonCollectors.java new file mode 100644 index 00000000..4cbf2e4e --- /dev/null +++ b/api/src/main/java/javax/json/stream/JsonCollectors.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json.stream; + +import java.util.Map; +import java.util.HashMap; +import java.util.stream.Collector; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.BiConsumer; + +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonArrayBuilder; +import javax.json.JsonObject; +import javax.json.JsonObjectBuilder; +import javax.json.JsonValue; +import javax.json.JsonException; + +/** + * This class contains some implementations of {@code java.util.stream.Collector} for accumulating + * {@link JsonValue}s into {@link JsonArray} and {@link JsonObject}. + * + * @since 1.1 + */ + +public final class JsonCollectors { + + private JsonCollectors() { + } + + /** + * Constructs a {@code java.util.stream.Collector} that accumulates the input {@code JsonValue} + * elements into a {@code JsonArray}. + * + * @return the constructed Collector + */ + public static Collector toJsonArray() { + return Collector.of( + Json::createArrayBuilder, + JsonArrayBuilder::add, + JsonArrayBuilder::addAll, + JsonArrayBuilder::build); + } + + /** + * Constructs a {@code java.util.stream.Collector} that accumulates the input {@code Map.Entry} + * elements into a {@code JsonObject}. + * + * @return the constructed Collector + */ + public static Collector, JsonObjectBuilder, JsonObject> toJsonObject() { + return Collector.of( + Json::createObjectBuilder, + (JsonObjectBuilder b, Map.Entry v) -> b.add(v.getKey(), v.getValue()), + JsonObjectBuilder::addAll, + JsonObjectBuilder::build); + } + + /** + * Constructs a {@code java.util.stream.Collector} that accumulates the input {@code JsonValue} + * elements into a {@code JsonObject}. The name/value pairs of the {@code JsonObject} are computed + * by applying the provided mapping functions. + * + * @param keyMapper a mapping function to produce names. + * @param valueMapper a mapping function to produce values + * @return the constructed Collector + */ + public static Collector + toJsonObject(Function keyMapper, + Function valueMapper) { + return Collector.of( + Json::createObjectBuilder, + (b, v) -> b.add(keyMapper.apply(v), valueMapper.apply(v)), + JsonObjectBuilder::addAll, + JsonObjectBuilder::build, + Collector.Characteristics.UNORDERED); + } + + /** + * Constructs a {@code java.util.stream.Collector} that implements a "group by" operation on the + * input {@code JsonValue} elements. A classifier function maps the input {@code JsonValue}s to keys, and + * the {@code JsonValue}s are partitioned into groups according to the value of the key. + * A reduction operation is performed on the {@code JsonValue}s in each group, using the + * downstream {@code Collector}. For each group, the key and the results of the reduction operation + * become the name/value pairs of the resultant {@code JsonObject}. + * + * @param the intermediate accumulation {@code JsonArrayBuilder} of the downstream collector + * @param classifier a function mapping the input {@code JsonValue}s to a String, producing keys + * @param downstream a {@code Collector} that implements a reduction operation on the + * {@code JsonValue}s in each group. + * @return the constructed {@code Collector} + */ + public static Collector, JsonObject> + groupingBy(Function classifier, + Collector downstream) { + + BiConsumer, JsonValue> accumulator = + (map, value) -> { + String key = classifier.apply(value); + if (key == null) { + throw new JsonException("element cannot be mapped to a null key"); + } + // Build a map of key to JsonArrayBuilder + T arrayBuilder = + map.computeIfAbsent(key, v->downstream.supplier().get()); + // Add elements from downstream Collector to the arrayBuilder. + downstream.accumulator().accept(arrayBuilder, value); + }; + Function, JsonObject> finisher = + map -> { + // transform the map of name: JsonArrayBuilder to + // name: JsonArray + // using the downstream collector for reducing the JsonArray + JsonObjectBuilder objectBuilder = Json.createObjectBuilder(); + map.forEach((k, v) -> { + JsonArray array = downstream.finisher().apply(v); + objectBuilder.add(k, array); + }); + return objectBuilder.build(); + }; + BinaryOperator> combiner = + (map1, map2) -> { + map1.putAll(map2); + return map1; + }; + return Collector.of(HashMap::new, accumulator, combiner, finisher, + Collector.Characteristics.UNORDERED); + } + + /** + * Constructs a {@code java.util.stream.Collector} that implements a "group by" operation on the + * input {@code JsonValue} elements. A classifier function maps the input {@code JsonValue}s to keys, and + * the {@code JsonValue}s are partitioned into groups according to the value of the key. + * The {@code JsonValue}s in each group are added to a {@code JsonArray}. The key and the + * {@code JsonArray} in each group becomes the name/value pair of the resultant {@code JsonObject}. + * + * @param classifier a function mapping the input {@code JsonValue}s to a String, producing keys + * @return the constructed {@code Collector} + */ + public static Collector, JsonObject> + groupingBy(Function classifier) { + return groupingBy(classifier, toJsonArray()); + } +} + diff --git a/api/src/main/java/javax/json/stream/JsonGenerationException.java b/api/src/main/java/javax/json/stream/JsonGenerationException.java new file mode 100644 index 00000000..e19b4e52 --- /dev/null +++ b/api/src/main/java/javax/json/stream/JsonGenerationException.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json.stream; + +import javax.json.JsonException; + +/** + * {@code JsonGenerationException} indicates an incorrect JSON is + * being generated. + */ +public class JsonGenerationException extends JsonException { + + /** + * Constructs a new runtime exception with the specified detail message. + * The cause is not initialized, and may subsequently be initialized by a + * call to {@link #initCause}. + * + * @param message the detail message. The detail message is saved for + * later retrieval by the {@link #getMessage()} method. + */ + public JsonGenerationException(String message) { + super(message); + } + + /** + * Constructs a new runtime exception with the specified detail message and + * cause.

Note that the detail message associated with + * {@code cause} is not automatically incorporated in + * this runtime exception's detail message. + * + * @param message the detail message (which is saved for later retrieval + * by the {@link #getMessage()} method). + * @param cause the cause (which is saved for later retrieval by the + * {@link #getCause()} method). (A null value is + * permitted, and indicates that the cause is nonexistent or + * unknown.) + */ + public JsonGenerationException(String message, Throwable cause) { + super(message, cause); + } + +} + diff --git a/api/src/main/java/javax/json/stream/JsonGenerator.java b/api/src/main/java/javax/json/stream/JsonGenerator.java new file mode 100644 index 00000000..1c309c2a --- /dev/null +++ b/api/src/main/java/javax/json/stream/JsonGenerator.java @@ -0,0 +1,539 @@ +/* + * Copyright (c) 2011, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json.stream; + +import javax.json.JsonValue; +import java.io.Closeable; +import java.io.Flushable; +import java.math.BigDecimal; +import java.math.BigInteger; + +/** + * Writes JSON data to an output source in a streaming way. The class + * {@link javax.json.Json} contains methods to create generators for character + * or output streams ({@link java.io.Writer} and {@link java.io.OutputStream}). + * + *

+ * The following example shows how to create a JSON generator: + *

+ * 
+ * JsonGenerator generator = Json.createGenerator(...);
+ * 
+ * 
+ * + *

+ * The class {@link JsonGeneratorFactory} also contains methods to create + * {@code JsonGenerator} instances. {@link JsonGeneratorFactory} should be used + * when creating multiple generator instances, as in the following example: + *

+ * 
+ * JsonGeneratorFactory factory = Json.createGeneratorFactory();
+ * JsonGenerator generator1 = factory.createGenerator(...);
+ * JsonGenerator generator2 = factory.createGenerator(...);
+ * 
+ * 
+ * + *

+ * JSON objects can be created using {@code JsonGenerator} by calling the + * {@link #writeStartObject()} method and then adding name/value pairs with the + * {@code write} method. + *

+ * The following example shows how to generate an empty JSON object: + *

+ * 
+ * JsonGenerator generator = ...;
+ * generator.writeStartObject().writeEnd().close();
+ * 
+ * 
+ * + * JSON arrays can be created using {@code JsonGenerator} by calling the + * {@link #writeStartArray()} method and then adding values with the + * {@code write} method. + * + *

+ * The following example shows how to generate an empty JSON array: + *

+ * 
+ * JsonGenerator generator = ...;
+ * generator.writeStartArray().writeEnd().close();
+ * 
+ * 
+ * + *

+ * Other JSON values (that are not JSON objects or arrays) can be created + * by calling the appropiate {@code write} methods. + *

+ * The following example shows how to generate a JSON string: + *


+ * JsonGenerator generator = ...;
+ * generator.write("message").close();
+ * 
+ * + * {@code JsonGenerator} methods can be chained as in the following example: + *
+ * 
+ * generator
+ *     .writeStartObject()
+ *         .write("firstName", "John")
+ *         .write("lastName", "Smith")
+ *         .write("age", 25)
+ *         .writeStartObject("address")
+ *             .write("streetAddress", "21 2nd Street")
+ *             .write("city", "New York")
+ *             .write("state", "NY")
+ *             .write("postalCode", "10021")
+ *         .writeEnd()
+ *         .writeStartArray("phoneNumber")
+ *             .writeStartObject()
+ *                 .write("type", "home")
+ *                 .write("number", "212 555-1234")
+ *             .writeEnd()
+ *             .writeStartObject()
+ *                 .write("type", "fax")
+ *                 .write("number", "646 555-4567")
+ *             .writeEnd()
+ *         .writeEnd()
+ *     .writeEnd();
+ * generator.close();
+ * 
+ * 
+ * + * The example code above generates the following JSON (or equivalent): + *
+ * 
+ * {
+ *   "firstName": "John", "lastName": "Smith", "age": 25,
+ *   "address" : {
+ *       "streetAddress": "21 2nd Street",
+ *       "city": "New York",
+ *       "state": "NY",
+ *       "postalCode": "10021"
+ *   },
+ *   "phoneNumber": [
+ *       {"type": "home", "number": "212 555-1234"},
+ *       {"type": "fax", "number": "646 555-4567"}
+ *    ]
+ * }
+ * 
+ * 
+ * + * The generated JSON text must strictly conform to the grammar defined in + * RFC 7159. + * + * @see javax.json.Json + * @see JsonGeneratorFactory + */ +public interface JsonGenerator extends Flushable, /*Auto*/Closeable { + /** + * Configuration property to generate JSON prettily. All providers + * must support this property. The value of the property could be + * be anything. + */ + String PRETTY_PRINTING = "javax.json.stream.JsonGenerator.prettyPrinting" ; + + /** + * Writes the JSON start object character. It starts a new child object + * context within which JSON name/value pairs can be written to the object. + * This method is valid only in an array context, field context or in no context (when a + * context is not yet started). This method can only be called once in + * no context. + * + * @return this generator + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonGenerationException if this method is called within an + * object context or if it is called more than once in no context. + */ + JsonGenerator writeStartObject(); + + /** + * Writes the JSON name/start object character pair in the current + * object context. It starts a new child object context within which JSON + * name/value pairs can be written to the object. + * + * @param name a name within the JSON name/object pair to be written + * @return this generator + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonGenerationException if this method is not called within an + * object context + */ + JsonGenerator writeStartObject(String name); + + /** + * Writes the JSON name with a colon. It starts a field context, in which valid + * options are writing a value, starting an object or an array. + * + * Writing value closes field context, if object or array is started after field name, + * field context will be closed after object/array close. + * + * @param name name of json field + * @return this generator + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonGenerationException if this method is not called within an + * object context + * + * @since 1.1 + */ + JsonGenerator writeKey(String name); + + /** + * Writes the JSON start array character. It starts a new child array + * context within which JSON values can be written to the array. This + * method is valid only in an array context, field context or in no context (when a + * context is not yet started). This method can only be called once in + * no context. + * + * @return this generator + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonGenerationException if this method is called within an + * object context or if called more than once in no context + */ + JsonGenerator writeStartArray(); + + /** + * Writes the JSON name/start array character pair with in the current + * object context. It starts a new child array context within which JSON + * values can be written to the array. + * + * @param name a name within the JSON name/array pair to be written + * @return this generator + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonGenerationException if this method is not called within + * an object context + */ + JsonGenerator writeStartArray(String name); + + /** + * Writes a JSON name/value pair in the current object context. + * + * @param name a name in the JSON name/value pair to be written in + * current JSON object + * @param value a value in the JSON name/value pair to be written in + * current JSON object + * @return this generator + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonGenerationException if this method is not called within an + * object context + */ + JsonGenerator write(String name, JsonValue value); + + /** + * Writes a JSON name/string value pair in the current object context. + * The specified value is written as JSON string value. + * + * @param name a name in the JSON name/string pair to be written in + * current JSON object + * @param value a value in the JSON name/string pair to be written in + * current JSON object + * @return this generator + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonGenerationException if this method is not called within an + * object context + */ + JsonGenerator write(String name, String value); + + /** + * Writes a JSON name/number value pair in the current object context. + * The specified value is written as a JSON number value. The string + * {@code new BigDecimal(value).toString()} + * is used as the text value for writing. + * + * @param name a name in the JSON name/number pair to be written in + * current JSON object + * @param value a value in the JSON name/number pair to be written in + * current JSON object + * @return this generator + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonGenerationException if this method is not called within an + * object context. + */ + JsonGenerator write(String name, BigInteger value); + + /** + * Writes a JSON name/number value pair in the current object context. + * The specified value is written as a JSON number value. The specified + * value's {@code toString()} is used as the text value for writing. + * + * @param name a name in the JSON name/number pair to be written in + * current JSON object + * @param value a value in the JSON name/number pair to be written in + * current JSON object + * @return this generator + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonGenerationException if this method is not called within an + * object context. + */ + JsonGenerator write(String name, BigDecimal value); + + /** + * Writes a JSON name/number value pair in the current object context. + * The specified value is written as a JSON number value. The string + * {@code new BigDecimal(value).toString()} is used as the text value + * for writing. + * + * @param name a name in the JSON name/number pair to be written in + * current JSON object + * @param value a value in the JSON name/number pair to be written in + * current JSON object + * @return this generator + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonGenerationException if this method is not called within an + * object context. + */ + JsonGenerator write(String name, int value); + + /** + * Writes a JSON name/number value pair in the current object context. + * The specified value is written as a JSON number value. The string + * {@code new BigDecimal(value).toString()} is used as the text + * value for writing. + * + * @param name a name in the JSON name/number pair to be written in + * current JSON object + * @param value a value in the JSON name/number pair to be written in + * current JSON object + * @return this generator + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonGenerationException if this method is not called within an + * object context. + */ + JsonGenerator write(String name, long value); + + /** + * Writes a JSON name/number value pair in the current object context. + * The specified value is written as a JSON number value. The string + * {@code BigDecimal.valueOf(double).toString()} + * is used as the text value for writing. + * + * @param name a name in the JSON name/number pair to be written in + * current JSON object + * @param value a value in the JSON name/number pair to be written in + * current JSON object + * @return this generator + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws NumberFormatException if the value is Not-a-Number (NaN) or infinity. + * @throws JsonGenerationException if this method is not called within an + * object context + */ + JsonGenerator write(String name, double value); + + /** + * Writes a JSON name/boolean value pair in the current object context. + * If value is true, it writes the JSON {@code true} value, otherwise + * it writes the JSON {@code false} value. + * + * @param name a name in the JSON name/boolean pair to be written in + * current JSON object + * @param value a value in the JSON name/boolean pair to be written in + * current JSON object + * @return this generator + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonGenerationException if this method is not called within an + * object context. + */ + JsonGenerator write(String name, boolean value); + + + /** + * Writes a JSON name/null value pair in an current object context. + * + * @param name a name in the JSON name/null pair to be written in + * current JSON object + * @return this generator + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonGenerationException if this method is not called within an + * object context + */ + JsonGenerator writeNull(String name); + + /** + * Writes the end of the current context. If the current context is + * an array context, this method writes the end-of-array character (']'). + * If the current context is an object context, this method writes the + * end-of-object character ('}'). After writing the end of the current + * context, the parent context becomes the new current context. + * If parent context is field context, it is closed. + * + * @return this generator + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonGenerationException if this method is called in no context. + */ + JsonGenerator writeEnd(); + + /** + * Writes the specified value as a JSON value within + * the current array, field or root context. + * + * @param value a value to be written in current JSON array + * @return this generator + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonGenerationException if this method is not called within an + * array or root context. + */ + JsonGenerator write(JsonValue value); + + /** + * Writes the specified value as a JSON string value within + * the current array, field or root context. + * + * @param value a value to be written in current JSON array + * @return this generator + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonGenerationException if this method is not called within an + * array or root context. + */ + JsonGenerator write(String value); + + /** + * Writes the specified value as a JSON number value within + * the current array, field or root context. The specified value's {@code toString()} + * is used as the the text value for writing. + * + * @param value a value to be written in current JSON array + * @return this generator + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonGenerationException if this method is not called within an + * array or root context. + * + * @see javax.json.JsonNumber + */ + JsonGenerator write(BigDecimal value); + + /** + * Writes the specified value as a JSON number value within + * the current array, field or root context. The string {@code new BigDecimal(value).toString()} + * is used as the text value for writing. + * + * @param value a value to be written in current JSON array + * @return this generator. + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonGenerationException if this method is not called within an + * array or root context. + * + * @see javax.json.JsonNumber + */ + JsonGenerator write(BigInteger value); + + /** + * Writes the specified value as a JSON number value within + * the current array, field or root context. The string {@code new BigDecimal(value).toString()} + * is used as the text value for writing. + * + * @param value a value to be written in current JSON array + * @return this generator + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonGenerationException if this method is not called within an + * array or root context. + */ + JsonGenerator write(int value); + + /** + * Writes the specified value as a JSON number value within + * the current array, field or root context. The string {@code new BigDecimal(value).toString()} + * is used as the text value for writing. + * + * @param value a value to be written in current JSON array + * @return this generator + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonGenerationException if this method is not called within an + * array or root context. + */ + JsonGenerator write(long value); + + /** + * Writes the specified value as a JSON number value within the current + * array, field or root context. The string {@code BigDecimal.valueOf(value).toString()} + * is used as the text value for writing. + * + * @param value a value to be written in current JSON array + * @return this generator + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonGenerationException if this method is not called within an + * array or root context. + * @throws NumberFormatException if the value is Not-a-Number (NaN) or infinity. + */ + JsonGenerator write(double value); + + /** + * Writes a JSON true or false value within the current array, field or root context. + * If value is true, this method writes the JSON {@code true} value, + * otherwise it writes the JSON {@code false} value. + * + * @param value a {@code boolean} value + * @return this generator + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonGenerationException if this method is not called within an + * array or root context. + */ + JsonGenerator write(boolean value); + + /** + * Writes a JSON null value within the current array, field or root context. + * + * @return this generator + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonGenerationException if this method is not called within an + * array or root context. + */ + JsonGenerator writeNull(); + + /** + * Closes this generator and frees any resources associated with it. + * This method closes the underlying output source. + * + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonGenerationException if an incomplete JSON is generated + */ + @Override + void close(); + + /** + * Flushes the underlying output source. If the generator has saved + * any characters in a buffer, writes them immediately to the underlying + * output source before flushing it. + * + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + */ + @Override + void flush(); + +} diff --git a/api/src/main/java/javax/json/stream/JsonGeneratorFactory.java b/api/src/main/java/javax/json/stream/JsonGeneratorFactory.java new file mode 100644 index 00000000..83b01ad0 --- /dev/null +++ b/api/src/main/java/javax/json/stream/JsonGeneratorFactory.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2011, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json.stream; + +import java.io.OutputStream; +import java.io.Writer; +import java.nio.charset.Charset; +import java.util.Map; + +/** + * Factory to create {@link JsonGenerator} instances. If a factory + * instance is configured with some configuration, the configuration applies + * to all generator instances created using that factory instance. + * + *

+ * The class {@link javax.json.Json Json} also provides methods to create + * {@link JsonGenerator} instances, but using {@code JsonGeneratorFactory} is + * preferred when creating multiple generator instances as shown in the + * following example: + * + *

+ * 
+ * JsonGeneratorFactory factory = Json.createGeneratorFactory();
+ * JsonGenerator generator1 = factory.createGenerator(...);
+ * JsonGenerator generator2 = factory.createGenerator(...);
+ * 
+ * 
+ * + *

All the methods in this class are safe for use by multiple concurrent + * threads. + */ +public interface JsonGeneratorFactory { + + /** + * Creates a JSON generator to write JSON text to a character stream. + * The generator is configured with the factory configuration. + * + * @param writer i/o writer to which JSON is written + * @return the created JSON generator + */ + JsonGenerator createGenerator(Writer writer); + + /** + * Creates a JSON generator to write JSON text to a byte stream. Characters + * written to the stream are encoded into bytes using UTF-8 encoding. + * The generator is configured with the factory's configuration. + * + * @param out i/o stream to which JSON is written + * @return the created JSON generator + */ + JsonGenerator createGenerator(OutputStream out); + + /** + * Creates a JSON generator to write JSON text to a byte stream. Characters + * written to the stream are encoded into bytes using the specified charset. + * The generator is configured with the factory's configuration. + * + * @param out i/o stream to which JSON is written + * @param charset a charset + * @return the created JSON generator + */ + JsonGenerator createGenerator(OutputStream out, Charset charset); + + /** + * Returns a read-only map of supported provider specific configuration + * properties that are used to configure the JSON generators. + * If there are any specified configuration properties that are not + * supported by the provider, they won't be part of the returned map. + * + * @return a map of supported provider specific properties that are used + * to configure the created generators. The map may be empty but not null + */ + Map getConfigInUse(); + +} diff --git a/api/src/main/java/javax/json/stream/JsonLocation.java b/api/src/main/java/javax/json/stream/JsonLocation.java new file mode 100644 index 00000000..6f0c12c2 --- /dev/null +++ b/api/src/main/java/javax/json/stream/JsonLocation.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json.stream; + +/** + * Provides the location information of a JSON event in an input source. The + * {@code JsonLocation} information can be used to identify incorrect JSON + * or can be used by higher frameworks to know about the processing location. + * + *

All the information provided by a {@code JsonLocation} is optional. For + * example, a provider may only report line numbers. Also, there may not be any + * location information for an input source. For example, if a + * {@code JsonParser} is created using + * {@link javax.json.JsonArray JsonArray} input source, all the methods in + * this class return -1. + * @see JsonParser + * @see JsonParsingException + */ +public interface JsonLocation { + + /** + * Return the line number (starts with 1 for the first line) for the current JSON event in the input source. + * + * @return the line number (starts with 1 for the first line) or -1 if none is available + */ + long getLineNumber(); + + /** + * Return the column number (starts with 1 for the first column) for the current JSON event in the input source. + * + * @return the column number (starts with 1 for the first column) or -1 if none is available + */ + long getColumnNumber(); + + /** + * Return the stream offset into the input source this location + * is pointing to. If the input source is a file or a byte stream then + * this is the byte offset into that stream, but if the input source is + * a character media then the offset is the character offset. + * Returns -1 if there is no offset available. + * + * @return the offset of input source stream, or -1 if there is + * no offset available + */ + long getStreamOffset(); + +} diff --git a/api/src/main/java/javax/json/stream/JsonParser.java b/api/src/main/java/javax/json/stream/JsonParser.java new file mode 100644 index 00000000..67e5aecd --- /dev/null +++ b/api/src/main/java/javax/json/stream/JsonParser.java @@ -0,0 +1,487 @@ +/* + * Copyright (c) 2011, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json.stream; + + +import java.io.Closeable; +import java.math.BigDecimal; +import java.util.stream.Stream; +import java.util.Map; + +import javax.json.JsonValue; +import javax.json.JsonObject; +import javax.json.JsonArray; + +/** + * Provides forward, read-only access to JSON data in a streaming way. This + * is the most efficient way for reading JSON data. + * This is the only way to parse and process JSON data that are too big to be loaded in memory. + *

The class + * {@link javax.json.Json} contains methods to create parsers from input + * sources ({@link java.io.InputStream} and {@link java.io.Reader}). + * + *

+ * The following example demonstrates how to create a parser from a string + * that contains an empty JSON array: + *

+ * 
+ * JsonParser parser = Json.createParser(new StringReader("[]"));
+ * 
+ * 
+ * + *

+ * The class {@link JsonParserFactory} also contains methods to create + * {@code JsonParser} instances. {@link JsonParserFactory} is preferred + * when creating multiple parser instances. A sample usage is shown + * in the following example: + *

+ * 
+ * JsonParserFactory factory = Json.createParserFactory();
+ * JsonParser parser1 = factory.createParser(...);
+ * JsonParser parser2 = factory.createParser(...);
+ * 
+ * 
+ * + *

+ * {@code JsonParser} parses JSON using the pull parsing programming model. + * In this model the client code controls the thread and calls the method + * {@code next()} to advance the parser to the next state after + * processing each element. The parser can generate the following events: + * {@code START_OBJECT}, {@code END_OBJECT}, {@code START_ARRAY}, + * {@code END_ARRAY}, {@code KEY_NAME}, {@code VALUE_STRING}, + * {@code VALUE_NUMBER}, {@code VALUE_TRUE}, {@code VALUE_FALSE}, + * and {@code VALUE_NULL}. + * + *

+ * For example, for an empty JSON object ({ }), the parser generates the event + * {@code START_OBJECT} with the first call to the method {@code next()} and the + * event {@code END_OBJECT} with the second call to the method {@code next()}. + * The following code demonstrates how to access these events: + * + *

+ * 
+ * Event event = parser.next(); // START_OBJECT
+ * event = parser.next();       // END_OBJECT
+ * 
+ * 
+ * + *

+ * For example, for the following JSON: + *

+ * {
+ *   "firstName": "John", "lastName": "Smith", "age": 25,
+ *   "phoneNumber": [
+ *       { "type": "home", "number": "212 555-1234" },
+ *       { "type": "fax", "number": "646 555-4567" }
+ *    ]
+ * }
+ * 
+ * + *

calls to the method {@code next()} result in parse events at the specified + * locations below (marked in bold): + * + *

+ * {START_OBJECT
+ *   "firstName"KEY_NAME: "John"VALUE_STRING, "lastName"KEY_NAME: "Smith"VALUE_STRING, "age"KEY_NAME: 25VALUE_NUMBER,
+ *   "phoneNumber"KEY_NAME : [START_ARRAY
+ *       {START_OBJECT "type"KEY_NAME: "home"VALUE_STRING, "number"KEY_NAME: "212 555-1234"VALUE_STRING }END_OBJECT,
+ *       {START_OBJECT "type"KEY_NAME: "fax"VALUE_STRING, "number"KEY_NAME: "646 555-4567"VALUE_STRING }END_OBJECT
+ *    ]END_ARRAY
+ * }END_OBJECT
+ * 
+ * + * The methods {@link #next()} and {@link #hasNext()} enable iteration over + * parser events to process JSON data. {@code JsonParser} provides get methods + * to obtain the value at the current state of the parser. For example, the + * following code shows how to obtain the value "John" from the JSON above: + * + *
+ * 
+ * Event event = parser.next(); // START_OBJECT
+ * event = parser.next();       // KEY_NAME
+ * event = parser.next();       // VALUE_STRING
+ * parser.getString();          // "John"
+ * 
+ * 
+ * + * Starting in version 1.1, it is possible to build a partial JSON object + * model from the stream, at the current parser position. + * The methods {@link #getArray} and {@link #getObject} can be used to read in + * a {@code JsonArray} or {@code JsonObject}. For example, the following code + * shows how to obtain the phoneNumber in a JsonArray, from the JSON above: + * + *

+ * while (parser.hasNext() {
+ *     Event event = parser.next();
+ *     if (event == JsonParser.Event.KEY_NAME ) {
+ *         String key = getString();
+ *         event = parser.next();
+ *         if (key.equals("phoneNumber") {
+ *             JsonArray phones = parser.getArray();
+ *         }
+ *     }
+ * }
+ * 
+ * + * The methods {@link #getArrayStream} and {@link #getObjectStream} can be used + * to get a stream of the elements of a {@code JsonArray} or {@code JsonObject}. + * For example, the following code shows another way to obtain John's phoneNumber + * in a {@code JsonArray} : + * + *
{@code
+ * Event event = parser.next(); // START_OBJECT
+ * JsonArray phones = (JsonArray)
+ *     parser.getObjectStream().filter(e->e.getKey().equals("phoneNumber"))
+ *                             .map(e->e.getValue())
+ *                             .findFirst()
+ *                             .get();
+ * }
+ * + * The methods {@link #skipArray} and {@link #skipObject} can be used to + * skip tokens and position the parser to {@code END_ARRAY} or + * {@code END_OBJECT}. + *

+ * {@code JsonParser} can be used to parse sequence of JSON values that are not + * enclosed in a JSON array, e.g. { } { }. The following code demonstrates how + * to parse such sequence. + *


+ * JsonParser parser = Json.createParser(...);
+ * while (parser.hasNext) {
+ *     parser.next(); // advance parser state
+ *     JsonValue value = parser.getValue();
+ * }
+ * 
+ * + * @see javax.json.Json + * @see JsonParserFactory + */ +public interface JsonParser extends /*Auto*/Closeable { + + /** + * An event from {@code JsonParser}. + */ + enum Event { + /** + * Start of a JSON array. The position of the parser is after '['. + */ + START_ARRAY, + /** + * Start of a JSON object. The position of the parser is after '{'. + */ + START_OBJECT, + /** + * Name in a name/value pair of a JSON object. The position of the parser + * is after the key name. The method {@link #getString} returns the key + * name. + */ + KEY_NAME, + /** + * String value in a JSON array or object. The position of the parser is + * after the string value. The method {@link #getString} + * returns the string value. + */ + VALUE_STRING, + /** + * Number value in a JSON array or object. The position of the parser is + * after the number value. {@code JsonParser} provides the following + * methods to access the number value: {@link #getInt}, + * {@link #getLong}, and {@link #getBigDecimal}. + */ + VALUE_NUMBER, + /** + * {@code true} value in a JSON array or object. The position of the + * parser is after the {@code true} value. + */ + VALUE_TRUE, + /** + * {@code false} value in a JSON array or object. The position of the + * parser is after the {@code false} value. + */ + VALUE_FALSE, + /** + * {@code null} value in a JSON array or object. The position of the + * parser is after the {@code null} value. + */ + VALUE_NULL, + /** + * End of a JSON object. The position of the parser is after '}'. + */ + END_OBJECT, + /** + * End of a JSON array. The position of the parser is after ']'. + */ + END_ARRAY + } + + /** + * Returns {@code true} if there are more parsing states. This method returns + * {@code false} if the parser reaches the end of the JSON text. + * + * @return {@code true} if there are more parsing states. + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonParsingException if the parser encounters invalid JSON + * when advancing to next state. + */ + boolean hasNext(); + + /** + * Returns the event for the next parsing state. + * + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + * @throws JsonParsingException if the parser encounters invalid JSON + * when advancing to next state. + * @throws java.util.NoSuchElementException if there are no more parsing + * states. + * @return the event for the next parsing state + */ + Event next(); + + /** + * Returns a {@code String} for the name in a name/value pair, + * for a string value or a number value. This method should only be called + * when the parser state is {@link Event#KEY_NAME}, {@link Event#VALUE_STRING}, + * or {@link Event#VALUE_NUMBER}. + * + * @return a name when the parser state is {@link Event#KEY_NAME} + * a string value when the parser state is {@link Event#VALUE_STRING} + * a number value when the parser state is {@link Event#VALUE_NUMBER} + * @throws IllegalStateException when the parser state is not + * {@code KEY_NAME}, {@code VALUE_STRING}, or {@code VALUE_NUMBER} + */ + String getString(); + + /** + * Returns true if the JSON number at the current parser state is a + * integral number. A {@link BigDecimal} may be used to store the value + * internally and this method semantics are defined using its + * {@code scale()}. If the scale is zero, then it is considered integral + * type. This integral type information can be used to invoke an + * appropriate accessor method to obtain a numeric value as in the + * following example: + * + *
+     * 
+     * JsonParser parser = ...
+     * if (parser.isIntegralNumber()) {
+     *     parser.getInt();     // or other methods to get integral value
+     * } else {
+     *     parser.getBigDecimal();
+     * }
+     * 
+     * 
+ * + * @return true if this number is a integral number, otherwise false + * @throws IllegalStateException when the parser state is not + * {@code VALUE_NUMBER} + */ + boolean isIntegralNumber(); + + /** + * Returns a JSON number as an integer. The returned value is equal + * to {@code new BigDecimal(getString()).intValue()}. Note that + * this conversion can lose information about the overall magnitude + * and precision of the number value as well as return a result with + * the opposite sign. This method should only be called when the parser + * state is {@link Event#VALUE_NUMBER}. + * + * @return an integer for a JSON number + * @throws IllegalStateException when the parser state is not + * {@code VALUE_NUMBER} + * @see java.math.BigDecimal#intValue() + */ + int getInt(); + + /** + * Returns a JSON number as a long. The returned value is equal + * to {@code new BigDecimal(getString()).longValue()}. Note that this + * conversion can lose information about the overall magnitude and + * precision of the number value as well as return a result with + * the opposite sign. This method is only called when the parser state is + * {@link Event#VALUE_NUMBER}. + * + * @return a long for a JSON number + * @throws IllegalStateException when the parser state is not + * {@code VALUE_NUMBER} + * @see java.math.BigDecimal#longValue() + */ + long getLong(); + + /** + * Returns a JSON number as a {@code BigDecimal}. The {@code BigDecimal} + * is created using {@code new BigDecimal(getString())}. This + * method should only called when the parser state is + * {@link Event#VALUE_NUMBER}. + * + * @return a {@code BigDecimal} for a JSON number + * @throws IllegalStateException when the parser state is not + * {@code VALUE_NUMBER} + */ + BigDecimal getBigDecimal(); + + /** + * Return the location that corresponds to the parser's current state in + * the JSON input source. The location information is only valid in the + * current parser state (or until the parser is advanced to a next state). + * + * @return a non-null location corresponding to the current parser state + * in JSON input source + */ + JsonLocation getLocation(); + + /** + * Returns a {@code JsonObject} and advances the parser to the + * corresponding {@code END_OBJECT}. + * + * @return the {@code JsonObject} at the current parser position + * + * @throws IllegalStateException when the parser state is not + * {@code START_OBJECT} + * + * @since 1.1 + */ + default public JsonObject getObject() { + throw new UnsupportedOperationException(); + } + + /** + * Returns a {@code JsonValue} at the current parser position. + * If the parser state is {@code START_ARRAY}, the behavior is + * the same as {@link #getArray}. If the parser state is + * {@code START_OBJECT}, the behavior is the same as + * {@link #getObject}. For all other cases, if applicable, the JSON value is + * read and returned. + * + * @return the {@code JsonValue} at the current parser position. + * @throws IllegalStateException when the parser state is + * {@code END_OBJECT} or {@code END_ARRAY} + * + * @since 1.1 + */ + default public JsonValue getValue() { + throw new UnsupportedOperationException(); + } + + /** + * Returns a {@code JsonArray} and advance the parser to the + * the corresponding {@code END_ARRAY}. + * + * @return the {@code JsonArray} at the current parser position + * + * @throws IllegalStateException when the parser state is not + * {@code START_ARRAY} + * + * @since 1.1 + */ + default public JsonArray getArray() { + throw new UnsupportedOperationException(); + } + + /** + * Returns a stream of the {@code JsonArray} elements. + * The parser state must be {@code START_ARRAY}. + * The elements are read lazily, on an as-needed basis, as + * required by the stream operations. + * If the stream operations do not consume + * all of the array elements, {@link skipArray} can be used to + * skip the unprocessed array elements. + * + * @return a stream of elements of the {@code JsonArray} + * + * @throws IllegalStateException when the parser state is not + * {@code START_ARRAY} + * + * @since 1.1 + */ + default public Stream getArrayStream() { + throw new UnsupportedOperationException(); + } + + /** + * Returns a stream of the {@code JsonObject}'s + * name/value pairs. The parser state must be {@code START_OBJECT}. + * The name/value pairs are read lazily, on an as-needed basis, as + * required by the stream operations. + * If the stream operations do not consume + * all of the object's name/value pairs, {@link skipObject} can be + * used to skip the unprocessed elements. + * + * @return a stream of name/value pairs of the {@code JsonObject} + * + * @throws IllegalStateException when the parser state is not + * {@code START_OBJECT} + * + * @since 1.1 + */ + default public Stream> getObjectStream() { + throw new UnsupportedOperationException(); + } + + /** + * Returns a stream of {@code JsonValue} from a sequence of + * JSON values. The values are read lazily, on an as-needed basis, + * as needed by the stream operations. + * + * @return a Stream of {@code JsonValue} + * + * @throws IllegalStateException if the parser is in an array or object. + * + * @since 1.1 + */ + default public Stream getValueStream() { + throw new UnsupportedOperationException(); + } + + /** + * Advance the parser to {@code END_ARRAY}. + * If the parser is in array context, i.e. it has previously + * encountered a {@code START_ARRAY} without encountering the + * corresponding {@code END_ARRAY}, the parser is advanced to + * the corresponding {@code END_ARRAY}. + * If the parser is not in any array context, nothing happens. + * + * @since 1.1 + */ + default public void skipArray() { + throw new UnsupportedOperationException(); + } + + /** + * Advance the parser to {@code END_OBJECT}. + * If the parser is in object context, i.e. it has previously + * encountered a {@code START_OBJECT} without encountering the + * corresponding {@code END_OBJECT}, the parser is advanced to + * the corresponding {@code END_OBJECT}. + * If the parser is not in any object context, nothing happens. + * + * @since 1.1 + */ + default public void skipObject() { + throw new UnsupportedOperationException(); + } + + /** + * Closes this parser and frees any resources associated with the + * parser. This method closes the underlying input source. + * + * @throws javax.json.JsonException if an i/o error occurs (IOException + * would be cause of JsonException) + */ + @Override + void close(); +} diff --git a/api/src/main/java/javax/json/stream/JsonParserFactory.java b/api/src/main/java/javax/json/stream/JsonParserFactory.java new file mode 100644 index 00000000..32a43259 --- /dev/null +++ b/api/src/main/java/javax/json/stream/JsonParserFactory.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2011, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json.stream; + +import javax.json.JsonArray; +import javax.json.JsonObject; +import java.io.InputStream; +import java.io.Reader; +import java.nio.charset.Charset; +import java.util.Map; + +/** + * Factory for creating {@link JsonParser} instances. If a factory + * instance is configured with a configuration, the configuration applies + * to all parser instances created using that factory instance. + * + *

+ * The class {@link javax.json.Json Json} also provides methods to create + * {@link JsonParser} instances, but using {@code JsonParserFactory} is + * preferred when creating multiple parser instances as shown in the following + * example: + * + *

+ * 
+ * JsonParserFactory factory = Json.createParserFactory();
+ * JsonParser parser1 = factory.createParser(...);
+ * JsonParser parser2 = factory.createParser(...);
+ * 
+ * 
+ * + *

All the methods in this class are safe for use by multiple concurrent + * threads. + */ +public interface JsonParserFactory { + + /** + * Creates a JSON parser from a character stream. + * + * @param reader a i/o reader from which JSON is to be read + * @return the created JSON parser + */ + JsonParser createParser(Reader reader); + + /** + * Creates a JSON parser from the specified byte stream. + * The character encoding of the stream is determined + * as specified in RFC 7159. + * + * @param in i/o stream from which JSON is to be read + * @return the created JSON parser + * @throws javax.json.JsonException if encoding cannot be determined + * or i/o error (IOException would be cause of JsonException) + */ + JsonParser createParser(InputStream in); + + /** + * Creates a JSON parser from the specified byte stream. + * The bytes of the stream are decoded to characters using the + * specified charset. + * + * @param in i/o stream from which JSON is to be read + * @param charset a charset + * @return the created JSON parser + */ + JsonParser createParser(InputStream in, Charset charset); + + /** + * Creates a JSON parser from the specified JSON object. + * + * @param obj a JSON object + * @return the created JSON parser + */ + JsonParser createParser(JsonObject obj); + + /** + * Creates a JSON parser from the specified JSON array. + * + * @param array a JSON array + * @return the created JSON parser + */ + JsonParser createParser(JsonArray array); + + /** + * Returns a read-only map of supported provider specific configuration + * properties that are used to configure the JSON parsers. + * If there are any specified configuration properties that are not + * supported by the provider, they won't be part of the returned map. + * + * @return a map of supported provider specific properties that are used + * to configure the created parsers. The map may be empty but not null + */ + Map getConfigInUse(); + +} diff --git a/api/src/main/java/javax/json/stream/JsonParsingException.java b/api/src/main/java/javax/json/stream/JsonParsingException.java new file mode 100644 index 00000000..9677438f --- /dev/null +++ b/api/src/main/java/javax/json/stream/JsonParsingException.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.json.stream; + +import javax.json.JsonException; + +/** + * {@code JsonParsingException} is used when an incorrect JSON is + * being parsed. + */ +public class JsonParsingException extends JsonException { + + private final JsonLocation location; + + /** + * Constructs a new runtime exception with the specified detail message. + * The cause is not initialized, and may subsequently be initialized by a + * call to {@link #initCause}. + * + * @param message the detail message. The detail message is saved for + * later retrieval by the {@link #getMessage()} method. + * @param location the location of the incorrect JSON + */ + public JsonParsingException(String message, JsonLocation location) { + super(message); + this.location = location; + } + + /** + * Constructs a new runtime exception with the specified detail message and + * cause.

Note that the detail message associated with + * {@code cause} is not automatically incorporated in + * this runtime exception's detail message. + * + * @param message the detail message (which is saved for later retrieval + * by the {@link #getMessage()} method). + * @param cause the cause (which is saved for later retrieval by the + * {@link #getCause()} method). (A null value is + * permitted, and indicates that the cause is nonexistent or + * unknown.) + * @param location the location of the incorrect JSON + */ + public JsonParsingException(String message, Throwable cause, JsonLocation location) { + super(message, cause); + this.location = location; + } + + /** + * Return the location of the incorrect JSON. + * + * @return the non-null location of the incorrect JSON + */ + public JsonLocation getLocation() { + return location; + } + +} + diff --git a/api/src/main/java/javax/json/stream/package-info.java b/api/src/main/java/javax/json/stream/package-info.java new file mode 100644 index 00000000..33a2cf10 --- /dev/null +++ b/api/src/main/java/javax/json/stream/package-info.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +/** + * Provides a streaming API to parse and generate + * JSON. + * + *

+ * The streaming API consists of the interfaces + * {@link javax.json.stream.JsonParser} and + * {@link javax.json.stream.JsonGenerator}. The interface {@code JsonParser} + * contains methods to parse JSON in a streaming way. The interface + * {@code JsonGenerator} contains methods to write JSON to an output source + * in a streaming way. + * + *

+ * {@code JsonParser} provides forward, read-only access to JSON data using the + * pull parsing programming model. In this model the application code controls + * the thread and calls methods in the parser interface to move the parser + * forward or to obtain JSON data from the current state of the parser. + * + *

+ * {@code JsonGenerator} provides methods to write JSON to an output source. + * The generator writes name/value pairs in JSON objects and values in JSON + * arrays. + * + *

+ * The streaming API is a low-level API designed to process large amounts of + * JSON data efficiently. Other JSON frameworks (such as JSON binding) can be + * implemented using this API. + * + * @since JSON Processing 1.0 + */ +package javax.json.stream; diff --git a/api/src/main/javadoc/overview.html b/api/src/main/javadoc/overview.html new file mode 100644 index 00000000..32cf7466 --- /dev/null +++ b/api/src/main/javadoc/overview.html @@ -0,0 +1,108 @@ + + + + +The Java API for JSON Processing provides portable APIs to parse, +generate, transform, and query JSON using the +streaming API or the object model API. + +

The Streaming API provides a way to parsing and generation of JSON in a +streaming fashion. It hands over parsing and generation control to the +programmer. The streaming API provides an event-based parser and allows an +application developer to ask for the next event rather than handling the event +in a callback. This gives a developer more procedural control over +the processing of the JSON. Application code can process or discard +the parser event, and ask for the next event(pull the event). The +streaming model is adequate for local processing where random access of other +parts of the data is not required. Similarly, the streaming API provides +a way to generate well-formed JSON to a stream by writing one event at a time. + +

The object model API creates a random access tree-like structure that +represents the JSON data in memory. The tree can then be navigated and +queried. This programming model is the most flexible and enables processing +that requires random access to the complete contents of the tree. However, +it is often not as efficient as the streaming model and requires more memory. +The object model generates JSON output by navigating the entire tree at once. + +

The Streaming API

+

The streaming API is similar to the StAX API for XML and consists of the +interfaces {@link javax.json.stream.JsonParser} and +{@link javax.json.stream.JsonGenerator}. {@code JsonParser} +contains methods to parse JSON data using the streaming model. +{@code JsonGenerator} contains methods to write JSON data to an ouptut source. + +

{@code JsonParser} provides forward, read-only access to +JSON data using the pull parsing programming model. In this model the +application code controls the thread and calls methods in the parser interface +to move the parser forward or to obtain JSON data from the current state of +the parser. Refer to +this example +for more details. + +

{@code JsonGenerator} provides methods to write JSON to a stream. The +generator writes name/value pairs in JSON objects and values in JSON arrays. +Refer to +this +example for more details. + +

The streaming API is a low-level API designed to process large amounts of +JSON data efficiently. Other JSON frameworks (such as JSON binding) can be +implemented using this API.

+ +

The Object Model API

+

The object model API is similar to the DOM API for XML. It is a high-level +API that provides immutable object models for JSON object and array structures. +These JSON structures are represented as object models using the Java types +{@link javax.json.JsonObject} and {@link javax.json.JsonArray}. +{@code JsonObject} provides a {@link java.util.Map} view to access the unordered +collection of zero or more name/value pairs from the model. Similarly, +{@code JsonArray} provides a {@link java.util.List} view to access the ordered +sequence of zero or more values from the model. + +

The object model API uses builder patterns to create these object models. +Application code can use the interface {@link javax.json.JsonObjectBuilder} +to create models that represent JSON objects. The resulting model is of type +{@code JsonObject}. Refer to +this example +for more details. Application code can use the interface +{@link javax.json.JsonArrayBuilder} to create models that represent JSON arrays. +The resulting model is of type {@code JsonArray}. Refer to +this example +for more details. + +

These object models can also be created from an input source (such as +{@link java.io.InputStream} or {@link java.io.Reader}) using the interface +{@link javax.json.JsonReader}. +This example shows +how to read and create an empty {@code JsonArray} model using the interface +{@code JsonReader}. Similarly, these object models can be written to an output +source (such as {@link java.io.OutputStream} or {@link java.io.Writer}) using +the class {@link javax.json.JsonWriter}. +This example shows +how to write an empty {@code JsonObject} model using the interface +{@code JsonWriter}. + +

JSON Pointer, JSON Patch, and JSON Merge Patch

+The Java API for JSON Processing supports the latest standard on +JSON Pointer, +JSON Patch, and +JSON Merge Patch. + + + diff --git a/api/src/main/jdk9/module-info.java b/api/src/main/jdk9/module-info.java new file mode 100644 index 00000000..fe69e93c --- /dev/null +++ b/api/src/main/jdk9/module-info.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2016, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +module java.json { + exports javax.json; + exports javax.json.spi; + exports javax.json.stream; + uses javax.json.spi.JsonProvider; +} diff --git a/bundles/licensee/pom.xml b/bundles/licensee/pom.xml new file mode 100755 index 00000000..e5258e82 --- /dev/null +++ b/bundles/licensee/pom.xml @@ -0,0 +1,85 @@ + + + + + 4.0.0 + + org.glassfish + json-bundles + 1.2-SNAPSHOT + + + json-licensee-bundle + Licensee source bundle + pom + + + + org.glassfish + javax.json + + + + + + org.codehaus.mojo + wagon-maven-plugin + + + get-license + package + + download-single + + + ${license.url} + TLDA_SCSL_Licensees_License_Notice.txt + ${assembly.directory} + + + + + + maven-assembly-plugin + + javax.json-ri-${impl_version}-src-licensee + + src/main/assembly/archive.xml + + false + + + + make-assembly + package + + single + + + + + + + + + ${project.build.directory}/assembly + http://hudson-sca.us.oracle.com/job/tlda-license/lastSuccessfulBuild/artifact + + + diff --git a/bundles/licensee/src/main/assembly/archive.xml b/bundles/licensee/src/main/assembly/archive.xml new file mode 100755 index 00000000..c4c26e7e --- /dev/null +++ b/bundles/licensee/src/main/assembly/archive.xml @@ -0,0 +1,46 @@ + + + + dist + + zip + + + + ../.. + + api/** + impl/** + + + api/target/** + impl/target/** + **/*.iml + + + + + target/assembly + + *.txt + + + + + diff --git a/bundles/pom.xml b/bundles/pom.xml new file mode 100644 index 00000000..06718583 --- /dev/null +++ b/bundles/pom.xml @@ -0,0 +1,48 @@ + + + + + 4.0.0 + + + org.glassfish + json + 1.2-SNAPSHOT + ../pom.xml + + + org.glassfish + json-bundles + pom + JSR 374 (JSON Processing) bundles + JSON Processing bundles + https://javaee.github.io/jsonp + + + ri + + + + licensee + + licensee + + + + diff --git a/bundles/ri/pom.xml b/bundles/ri/pom.xml new file mode 100755 index 00000000..17046bb8 --- /dev/null +++ b/bundles/ri/pom.xml @@ -0,0 +1,87 @@ + + + + + 4.0.0 + + org.glassfish + json-bundles + 1.2-SNAPSHOT + + + json-ri-bundle + RI bundle + pom + + + + javax.json + javax.json-api + + + org.glassfish + javax.json + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy + generate-sources + + copy-dependencies + + + false + ${assembly.directory} + + + + + + maven-assembly-plugin + + javax.json-ri-${impl_version} + + src/main/assembly/archive.xml + + false + + + + make-assembly + package + + single + + + + + + + + + ${project.build.directory}/assembly + + + diff --git a/bundles/ri/src/main/assembly/archive.xml b/bundles/ri/src/main/assembly/archive.xml new file mode 100755 index 00000000..725daaff --- /dev/null +++ b/bundles/ri/src/main/assembly/archive.xml @@ -0,0 +1,37 @@ + + + + dist + + zip + + + + src/main/resources + + + + target/assembly + + *.jar + + lib + + + diff --git a/bundles/ri/src/main/resources/LICENSE.md b/bundles/ri/src/main/resources/LICENSE.md new file mode 100644 index 00000000..5de3d1b4 --- /dev/null +++ b/bundles/ri/src/main/resources/LICENSE.md @@ -0,0 +1,637 @@ +# Eclipse Public License - v 2.0 + + THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE + PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION + OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + + 1. DEFINITIONS + + "Contribution" means: + + a) in the case of the initial Contributor, the initial content + Distributed under this Agreement, and + + b) in the case of each subsequent Contributor: + i) changes to the Program, and + ii) additions to the Program; + where such changes and/or additions to the Program originate from + and are Distributed by that particular Contributor. A Contribution + "originates" from a Contributor if it was added to the Program by + such Contributor itself or anyone acting on such Contributor's behalf. + Contributions do not include changes or additions to the Program that + are not Modified Works. + + "Contributor" means any person or entity that Distributes the Program. + + "Licensed Patents" mean patent claims licensable by a Contributor which + are necessarily infringed by the use or sale of its Contribution alone + or when combined with the Program. + + "Program" means the Contributions Distributed in accordance with this + Agreement. + + "Recipient" means anyone who receives the Program under this Agreement + or any Secondary License (as applicable), including Contributors. + + "Derivative Works" shall mean any work, whether in Source Code or other + form, that is based on (or derived from) the Program and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. + + "Modified Works" shall mean any work in Source Code or other form that + results from an addition to, deletion from, or modification of the + contents of the Program, including, for purposes of clarity any new file + in Source Code form that contains any contents of the Program. Modified + Works shall not include works that contain only declarations, + interfaces, types, classes, structures, or files of the Program solely + in each case in order to link to, bind by name, or subclass the Program + or Modified Works thereof. + + "Distribute" means the acts of a) distributing or b) making available + in any manner that enables the transfer of a copy. + + "Source Code" means the form of a Program preferred for making + modifications, including but not limited to software source code, + documentation source, and configuration files. + + "Secondary License" means either the GNU General Public License, + Version 2.0, or any later versions of that license, including any + exceptions or additional permissions as identified by the initial + Contributor. + + 2. GRANT OF RIGHTS + + a) Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free copyright + license to reproduce, prepare Derivative Works of, publicly display, + publicly perform, Distribute and sublicense the Contribution of such + Contributor, if any, and such Derivative Works. + + b) Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free patent + license under Licensed Patents to make, use, sell, offer to sell, + import and otherwise transfer the Contribution of such Contributor, + if any, in Source Code or other form. This patent license shall + apply to the combination of the Contribution and the Program if, at + the time the Contribution is added by the Contributor, such addition + of the Contribution causes such combination to be covered by the + Licensed Patents. The patent license shall not apply to any other + combinations which include the Contribution. No hardware per se is + licensed hereunder. + + c) Recipient understands that although each Contributor grants the + licenses to its Contributions set forth herein, no assurances are + provided by any Contributor that the Program does not infringe the + patent or other intellectual property rights of any other entity. + Each Contributor disclaims any liability to Recipient for claims + brought by any other entity based on infringement of intellectual + property rights or otherwise. As a condition to exercising the + rights and licenses granted hereunder, each Recipient hereby + assumes sole responsibility to secure any other intellectual + property rights needed, if any. For example, if a third party + patent license is required to allow Recipient to Distribute the + Program, it is Recipient's responsibility to acquire that license + before distributing the Program. + + d) Each Contributor represents that to its knowledge it has + sufficient copyright rights in its Contribution, if any, to grant + the copyright license set forth in this Agreement. + + e) Notwithstanding the terms of any Secondary License, no + Contributor makes additional grants to any Recipient (other than + those set forth in this Agreement) as a result of such Recipient's + receipt of the Program under the terms of a Secondary License + (if permitted under the terms of Section 3). + + 3. REQUIREMENTS + + 3.1 If a Contributor Distributes the Program in any form, then: + + a) the Program must also be made available as Source Code, in + accordance with section 3.2, and the Contributor must accompany + the Program with a statement that the Source Code for the Program + is available under this Agreement, and informs Recipients how to + obtain it in a reasonable manner on or through a medium customarily + used for software exchange; and + + b) the Contributor may Distribute the Program under a license + different than this Agreement, provided that such license: + i) effectively disclaims on behalf of all other Contributors all + warranties and conditions, express and implied, including + warranties or conditions of title and non-infringement, and + implied warranties or conditions of merchantability and fitness + for a particular purpose; + + ii) effectively excludes on behalf of all other Contributors all + liability for damages, including direct, indirect, special, + incidental and consequential damages, such as lost profits; + + iii) does not attempt to limit or alter the recipients' rights + in the Source Code under section 3.2; and + + iv) requires any subsequent distribution of the Program by any + party to be under a license that satisfies the requirements + of this section 3. + + 3.2 When the Program is Distributed as Source Code: + + a) it must be made available under this Agreement, or if the + Program (i) is combined with other material in a separate file or + files made available under a Secondary License, and (ii) the initial + Contributor attached to the Source Code the notice described in + Exhibit A of this Agreement, then the Program may be made available + under the terms of such Secondary Licenses, and + + b) a copy of this Agreement must be included with each copy of + the Program. + + 3.3 Contributors may not remove or alter any copyright, patent, + trademark, attribution notices, disclaimers of warranty, or limitations + of liability ("notices") contained within the Program from any copy of + the Program which they Distribute, provided that Contributors may add + their own appropriate notices. + + 4. COMMERCIAL DISTRIBUTION + + Commercial distributors of software may accept certain responsibilities + with respect to end users, business partners and the like. While this + license is intended to facilitate the commercial use of the Program, + the Contributor who includes the Program in a commercial product + offering should do so in a manner which does not create potential + liability for other Contributors. Therefore, if a Contributor includes + the Program in a commercial product offering, such Contributor + ("Commercial Contributor") hereby agrees to defend and indemnify every + other Contributor ("Indemnified Contributor") against any losses, + damages and costs (collectively "Losses") arising from claims, lawsuits + and other legal actions brought by a third party against the Indemnified + Contributor to the extent caused by the acts or omissions of such + Commercial Contributor in connection with its distribution of the Program + in a commercial product offering. The obligations in this section do not + apply to any claims or Losses relating to any actual or alleged + intellectual property infringement. In order to qualify, an Indemnified + Contributor must: a) promptly notify the Commercial Contributor in + writing of such claim, and b) allow the Commercial Contributor to control, + and cooperate with the Commercial Contributor in, the defense and any + related settlement negotiations. The Indemnified Contributor may + participate in any such claim at its own expense. + + For example, a Contributor might include the Program in a commercial + product offering, Product X. That Contributor is then a Commercial + Contributor. If that Commercial Contributor then makes performance + claims, or offers warranties related to Product X, those performance + claims and warranties are such Commercial Contributor's responsibility + alone. Under this section, the Commercial Contributor would have to + defend claims against the other Contributors related to those performance + claims and warranties, and if a court requires any other Contributor to + pay any damages as a result, the Commercial Contributor must pay + those damages. + + 5. NO WARRANTY + + EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT + PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS" + BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR + IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF + TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR + PURPOSE. Each Recipient is solely responsible for determining the + appropriateness of using and distributing the Program and assumes all + risks associated with its exercise of rights under this Agreement, + including but not limited to the risks and costs of program errors, + compliance with applicable laws, damage to or loss of data, programs + or equipment, and unavailability or interruption of operations. + + 6. DISCLAIMER OF LIABILITY + + EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT + PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS + SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST + PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE + EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGES. + + 7. GENERAL + + If any provision of this Agreement is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability of + the remainder of the terms of this Agreement, and without further + action by the parties hereto, such provision shall be reformed to the + minimum extent necessary to make such provision valid and enforceable. + + If Recipient institutes patent litigation against any entity + (including a cross-claim or counterclaim in a lawsuit) alleging that the + Program itself (excluding combinations of the Program with other software + or hardware) infringes such Recipient's patent(s), then such Recipient's + rights granted under Section 2(b) shall terminate as of the date such + litigation is filed. + + All Recipient's rights under this Agreement shall terminate if it + fails to comply with any of the material terms or conditions of this + Agreement and does not cure such failure in a reasonable period of + time after becoming aware of such noncompliance. If all Recipient's + rights under this Agreement terminate, Recipient agrees to cease use + and distribution of the Program as soon as reasonably practicable. + However, Recipient's obligations under this Agreement and any licenses + granted by Recipient relating to the Program shall continue and survive. + + Everyone is permitted to copy and distribute copies of this Agreement, + but in order to avoid inconsistency the Agreement is copyrighted and + may only be modified in the following manner. The Agreement Steward + reserves the right to publish new versions (including revisions) of + this Agreement from time to time. No one other than the Agreement + Steward has the right to modify this Agreement. The Eclipse Foundation + is the initial Agreement Steward. The Eclipse Foundation may assign the + responsibility to serve as the Agreement Steward to a suitable separate + entity. Each new version of the Agreement will be given a distinguishing + version number. The Program (including Contributions) may always be + Distributed subject to the version of the Agreement under which it was + received. In addition, after a new version of the Agreement is published, + Contributor may elect to Distribute the Program (including its + Contributions) under the new version. + + Except as expressly stated in Sections 2(a) and 2(b) above, Recipient + receives no rights or licenses to the intellectual property of any + Contributor under this Agreement, whether expressly, by implication, + estoppel or otherwise. All rights in the Program not expressly granted + under this Agreement are reserved. Nothing in this Agreement is intended + to be enforceable by any entity that is not a Contributor or Recipient. + No third-party beneficiary rights are created under this Agreement. + + Exhibit A - Form of Secondary Licenses Notice + + "This Source Code may also be made available under the following + Secondary Licenses when the conditions for such availability set forth + in the Eclipse Public License, v. 2.0 are satisfied: {name license(s), + version(s), and exceptions or additional permissions here}." + + Simply including a copy of this Agreement, including this Exhibit A + is not sufficient to license the Source Code under Secondary Licenses. + + If it is not possible or desirable to put the notice in a particular + file, then You may include the notice in a location (such as a LICENSE + file in a relevant directory) where a recipient would be likely to + look for such a notice. + + You may add additional accurate notices of copyright ownership. + +--- + +## The GNU General Public License (GPL) Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1335 + USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your freedom to + share and change it. By contrast, the GNU General Public License is + intended to guarantee your freedom to share and change free software--to + make sure the software is free for all its users. This General Public + License applies to most of the Free Software Foundation's software and + to any other program whose authors commit to using it. (Some other Free + Software Foundation software is covered by the GNU Library General + Public License instead.) You can apply it to your programs, too. + + When we speak of free software, we are referring to freedom, not price. + Our General Public Licenses are designed to make sure that you have the + freedom to distribute copies of free software (and charge for this + service if you wish), that you receive source code or can get it if you + want it, that you can change the software or use pieces of it in new + free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid anyone + to deny you these rights or to ask you to surrender the rights. These + restrictions translate to certain responsibilities for you if you + distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether gratis + or for a fee, you must give the recipients all the rights that you have. + You must make sure that they, too, receive or can get the source code. + And you must show them these terms so they know their rights. + + We protect your rights with two steps: (1) copyright the software, and + (2) offer you this license which gives you legal permission to copy, + distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain + that everyone understands that there is no warranty for this free + software. If the software is modified by someone else and passed on, we + want its recipients to know that what they have is not the original, so + that any problems introduced by others will not reflect on the original + authors' reputations. + + Finally, any free program is threatened constantly by software patents. + We wish to avoid the danger that redistributors of a free program will + individually obtain patent licenses, in effect making the program + proprietary. To prevent this, we have made it clear that any patent must + be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and + modification follow. + + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains a + notice placed by the copyright holder saying it may be distributed under + the terms of this General Public License. The "Program", below, refers + to any such program or work, and a "work based on the Program" means + either the Program or any derivative work under copyright law: that is + to say, a work containing the Program or a portion of it, either + verbatim or with modifications and/or translated into another language. + (Hereinafter, translation is included without limitation in the term + "modification".) Each licensee is addressed as "you". + + Activities other than copying, distribution and modification are not + covered by this License; they are outside its scope. The act of running + the Program is not restricted, and the output from the Program is + covered only if its contents constitute a work based on the Program + (independent of having been made by running the Program). Whether that + is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's source + code as you receive it, in any medium, provided that you conspicuously + and appropriately publish on each copy an appropriate copyright notice + and disclaimer of warranty; keep intact all the notices that refer to + this License and to the absence of any warranty; and give any other + recipients of the Program a copy of this License along with the Program. + + You may charge a fee for the physical act of transferring a copy, and + you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion of + it, thus forming a work based on the Program, and copy and distribute + such modifications or work under the terms of Section 1 above, provided + that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any part + thereof, to be licensed as a whole at no charge to all third parties + under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a notice + that there is no warranty (or else, saying that you provide a + warranty) and that users may redistribute the program under these + conditions, and telling the user how to view a copy of this License. + (Exception: if the Program itself is interactive but does not + normally print such an announcement, your work based on the Program + is not required to print an announcement.) + + These requirements apply to the modified work as a whole. If + identifiable sections of that work are not derived from the Program, and + can be reasonably considered independent and separate works in + themselves, then this License, and its terms, do not apply to those + sections when you distribute them as separate works. But when you + distribute the same sections as part of a whole which is a work based on + the Program, the distribution of the whole must be on the terms of this + License, whose permissions for other licensees extend to the entire + whole, and thus to each and every part regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or contest + your rights to work written entirely by you; rather, the intent is to + exercise the right to control the distribution of derivative or + collective works based on the Program. + + In addition, mere aggregation of another work not based on the Program + with the Program (or with a work based on the Program) on a volume of a + storage or distribution medium does not bring the other work under the + scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, + under Section 2) in object code or executable form under the terms of + Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections 1 + and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your cost + of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed + only for noncommercial distribution and only if you received the + program in object code or executable form with such an offer, in + accord with Subsection b above.) + + The source code for a work means the preferred form of the work for + making modifications to it. For an executable work, complete source code + means all the source code for all modules it contains, plus any + associated interface definition files, plus the scripts used to control + compilation and installation of the executable. However, as a special + exception, the source code distributed need not include anything that is + normally distributed (in either source or binary form) with the major + components (compiler, kernel, and so on) of the operating system on + which the executable runs, unless that component itself accompanies the + executable. + + If distribution of executable or object code is made by offering access + to copy from a designated place, then offering equivalent access to copy + the source code from the same place counts as distribution of the source + code, even though third parties are not compelled to copy the source + along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program + except as expressly provided under this License. Any attempt otherwise + to copy, modify, sublicense or distribute the Program is void, and will + automatically terminate your rights under this License. However, parties + who have received copies, or rights, from you under this License will + not have their licenses terminated so long as such parties remain in + full compliance. + + 5. You are not required to accept this License, since you have not + signed it. However, nothing else grants you permission to modify or + distribute the Program or its derivative works. These actions are + prohibited by law if you do not accept this License. Therefore, by + modifying or distributing the Program (or any work based on the + Program), you indicate your acceptance of this License to do so, and all + its terms and conditions for copying, distributing or modifying the + Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the + Program), the recipient automatically receives a license from the + original licensor to copy, distribute or modify the Program subject to + these terms and conditions. You may not impose any further restrictions + on the recipients' exercise of the rights granted herein. You are not + responsible for enforcing compliance by third parties to this License. + + 7. If, as a consequence of a court judgment or allegation of patent + infringement or for any other reason (not limited to patent issues), + conditions are imposed on you (whether by court order, agreement or + otherwise) that contradict the conditions of this License, they do not + excuse you from the conditions of this License. If you cannot distribute + so as to satisfy simultaneously your obligations under this License and + any other pertinent obligations, then as a consequence you may not + distribute the Program at all. For example, if a patent license would + not permit royalty-free redistribution of the Program by all those who + receive copies directly or indirectly through you, then the only way you + could satisfy both it and this License would be to refrain entirely from + distribution of the Program. + + If any portion of this section is held invalid or unenforceable under + any particular circumstance, the balance of the section is intended to + apply and the section as a whole is intended to apply in other + circumstances. + + It is not the purpose of this section to induce you to infringe any + patents or other property right claims or to contest validity of any + such claims; this section has the sole purpose of protecting the + integrity of the free software distribution system, which is implemented + by public license practices. Many people have made generous + contributions to the wide range of software distributed through that + system in reliance on consistent application of that system; it is up to + the author/donor to decide if he or she is willing to distribute + software through any other system and a licensee cannot impose that choice. + + This section is intended to make thoroughly clear what is believed to be + a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in + certain countries either by patents or by copyrighted interfaces, the + original copyright holder who places the Program under this License may + add an explicit geographical distribution limitation excluding those + countries, so that distribution is permitted only in or among countries + not thus excluded. In such case, this License incorporates the + limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new + versions of the General Public License from time to time. Such new + versions will be similar in spirit to the present version, but may + differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the Program + specifies a version number of this License which applies to it and "any + later version", you have the option of following the terms and + conditions either of that version or of any later version published by + the Free Software Foundation. If the Program does not specify a version + number of this License, you may choose any version ever published by the + Free Software Foundation. + + 10. If you wish to incorporate parts of the Program into other free + programs whose distribution conditions are different, write to the + author to ask for permission. For software which is copyrighted by the + Free Software Foundation, write to the Free Software Foundation; we + sometimes make exceptions for this. Our decision will be guided by the + two goals of preserving the free status of all derivatives of our free + software and of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO + WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. + EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR + OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, + EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE + ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH + YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL + NECESSARY SERVICING, REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN + WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY + AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR + DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL + DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM + (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED + INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF + THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR + OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest + possible use to the public, the best way to achieve this is to make it + free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest to + attach them to the start of each source file to most effectively convey + the exclusion of warranty; and each file should have at least the + "copyright" line and a pointer to where the full notice is found. + + One line to give the program's name and a brief idea of what it does. + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA + + Also add information on how to contact you by electronic and paper mail. + + If the program is interactive, make it output a short notice like this + when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type + `show w'. This is free software, and you are welcome to redistribute + it under certain conditions; type `show c' for details. + + The hypothetical commands `show w' and `show c' should show the + appropriate parts of the General Public License. Of course, the commands + you use may be called something other than `show w' and `show c'; they + could even be mouse-clicks or menu items--whatever suits your program. + + You should also get your employer (if you work as a programmer) or your + school, if any, to sign a "copyright disclaimer" for the program, if + necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + program `Gnomovision' (which makes passes at compilers) written by + James Hacker. + + signature of Ty Coon, 1 April 1989 + Ty Coon, President of Vice + + This General Public License does not permit incorporating your program + into proprietary programs. If your program is a subroutine library, you + may consider it more useful to permit linking proprietary applications + with the library. If this is what you want to do, use the GNU Library + General Public License instead of this License. + +--- + +## CLASSPATH EXCEPTION + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License version 2 cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from or + based on this library. If you modify this library, you may extend this + exception to your version of the library, but you are not obligated to + do so. If you do not wish to do so, delete this exception statement + from your version. diff --git a/bundles/ri/src/main/resources/README.txt b/bundles/ri/src/main/resources/README.txt new file mode 100644 index 00000000..c061627b --- /dev/null +++ b/bundles/ri/src/main/resources/README.txt @@ -0,0 +1,22 @@ +* javax.json-1.1.2.jar contains both "JSR 374 : Java API for JSON Processing 1.1" API and its default provider implementation. Keep it in classpath for both compiling and running your application. + +* If you are running with maven, you can use the following maven coordinates: + +for APIs: + + javax.json + javax.json-api + 1.1.2 + + +for reference implementation: + + + org.glassfish + javax.json + 1.1.2 + + +* GlassFish 5.x already bundles latest JSON Processing implementation and JAX-RS integration module. If you deploy an application with GlassFish 5.x, your application (war/ear) doesn't have to bundle APIs nor the ri jar. + +* Samples can be run from https://github.com/javaee/glassfish-samples diff --git a/copyright-exclude b/copyright-exclude new file mode 100644 index 00000000..3fb66811 --- /dev/null +++ b/copyright-exclude @@ -0,0 +1,9 @@ +.json +.md +/copyright-exclude +/copyright.txt +/META-INF/services +speclicense.html +/bundles/ri/src/main/resources/LICENSE.txt +/bundles/ri/src/main/resources/README.txt +/LICENSE.txt \ No newline at end of file diff --git a/demos/LICENSE.md b/demos/LICENSE.md new file mode 100644 index 00000000..c739f78b --- /dev/null +++ b/demos/LICENSE.md @@ -0,0 +1,29 @@ + + Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Eclipse Foundation, Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/demos/customprovider-jdk9/pom.xml b/demos/customprovider-jdk9/pom.xml new file mode 100644 index 00000000..4e4fe510 --- /dev/null +++ b/demos/customprovider-jdk9/pom.xml @@ -0,0 +1,94 @@ + + + + 4.0.0 + + + org.glassfish.jsonp + demos + 1.2-SNAPSHOT + ../pom.xml + + + http://maven.apache.org + customprovider-jdk9 + + customprovider-jdk9 + + + ${project.build.directory}/mods + + + customprovider-jdk9 + + + org.apache.maven.plugins + maven-dependency-plugin + + + prepare-endorsed + validate + + copy-dependencies + + + ${mod.dir} + false + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.20 + + + + test + + test + + + + + --module-path ${project.build.directory}/classes:${mod.dir}:${project.build.directory}/test-classes + --add-modules org.glassfish.java.json.demos.customprovider + + + + + + + + + javax.json + javax.json-api + + + junit + junit + test + + + org.hamcrest + hamcrest-core + 1.3 + test + + + diff --git a/demos/customprovider-jdk9/src/main/java/org/glassfish/json/customprovider/TestGenerator.java b/demos/customprovider-jdk9/src/main/java/org/glassfish/json/customprovider/TestGenerator.java new file mode 100644 index 00000000..f93fcc15 --- /dev/null +++ b/demos/customprovider-jdk9/src/main/java/org/glassfish/json/customprovider/TestGenerator.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0, which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.glassfish.json.customprovider; + +import javax.json.JsonException; +import javax.json.JsonValue; +import javax.json.stream.JsonGenerator; +import java.io.IOException; +import java.io.Writer; +import java.math.BigDecimal; +import java.math.BigInteger; + +/** + * @author Jitendra Kotamraju + */ +public class TestGenerator implements JsonGenerator { + private final Writer writer; + + public TestGenerator(Writer writer) { + this.writer = writer; + } + + @Override + public void flush() { + } + + @Override + public JsonGenerator writeStartObject() { + return null; + } + + @Override + public JsonGenerator writeStartObject(String name) { + return null; + } + + @Override + public JsonGenerator write(String name, String fieldValue) { + return null; + } + + @Override + public JsonGenerator write(String name, int value) { + return null; + } + + @Override + public JsonGenerator write(String name, long value) { + return null; + } + + @Override + public JsonGenerator write(String name, double value) { + return null; + } + + @Override + public JsonGenerator write(String name, BigInteger value) { + return null; + } + + @Override + public JsonGenerator write(String name, BigDecimal value) { + return null; + } + + @Override + public JsonGenerator write(String name, boolean value) { + return null; + } + + @Override + public JsonGenerator writeNull(String name) { + return null; + } + + @Override + public JsonGenerator write(JsonValue value) { + return null; + } + + @Override + public JsonGenerator writeStartArray() { + try { + writer.write("["); + } catch(IOException ioe) { + throw new JsonException("I/O error", ioe); + } + return this; + } + + @Override + public JsonGenerator writeStartArray(String name) { + return null; + } + + @Override + public JsonGenerator write(String name, JsonValue value) { + return null; + } + + @Override + public JsonGenerator write(String value) { + return null; + } + + + @Override + public JsonGenerator write(int value) { + return null; + } + + @Override + public JsonGenerator write(long value) { + return null; + } + + @Override + public JsonGenerator write(double value) { + return null; + } + + @Override + public JsonGenerator write(BigInteger value) { + return null; + } + + @Override + public JsonGenerator write(BigDecimal value) { + return null; + } + + @Override + public JsonGenerator write(boolean value) { + return null; + } + + @Override + public JsonGenerator writeNull() { + return null; + } + + @Override + public JsonGenerator writeEnd() { + try { + writer.write("]"); + } catch(IOException ioe) { + throw new JsonException("I/O error", ioe); + } + return this; + } + + @Override + public void close() { + try { + writer.close(); + } catch(IOException ioe) { + throw new JsonException("I/O error", ioe); + } + } + + @Override + public JsonGenerator writeKey(String string) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + +} diff --git a/demos/customprovider-jdk9/src/main/java/org/glassfish/json/customprovider/TestProvider.java b/demos/customprovider-jdk9/src/main/java/org/glassfish/json/customprovider/TestProvider.java new file mode 100644 index 00000000..0a97ffc9 --- /dev/null +++ b/demos/customprovider-jdk9/src/main/java/org/glassfish/json/customprovider/TestProvider.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0, which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.glassfish.json.customprovider; + +import javax.json.spi.JsonProvider; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonGeneratorFactory; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParserFactory; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.util.Map; +import javax.json.JsonArrayBuilder; +import javax.json.JsonBuilderFactory; +import javax.json.JsonObjectBuilder; +import javax.json.JsonReader; +import javax.json.JsonReaderFactory; +import javax.json.JsonWriter; +import javax.json.JsonWriterFactory; + +/** + * @author Jitendra Kotamraju + */ +public class TestProvider extends JsonProvider { + + @Override + public JsonGenerator createGenerator(Writer writer) { + return new TestGenerator(writer); + } + + @Override + public JsonGenerator createGenerator(OutputStream out) { + return new TestGenerator(new OutputStreamWriter(out)); + } + + @Override + public JsonGeneratorFactory createGeneratorFactory(Map config) { + return null; + } + + @Override + public JsonReader createReader(Reader reader) { + return null; + } + + @Override + public JsonReader createReader(InputStream in) { + return null; + } + + @Override + public JsonWriter createWriter(Writer writer) { + return null; + } + + @Override + public JsonWriter createWriter(OutputStream out) { + return null; + } + + @Override + public JsonWriterFactory createWriterFactory(Map config) { + return null; + } + + @Override + public JsonReaderFactory createReaderFactory(Map config) { + return null; + } + + @Override + public JsonObjectBuilder createObjectBuilder() { + return null; + } + + @Override + public JsonArrayBuilder createArrayBuilder() { + return null; + } + + @Override + public JsonBuilderFactory createBuilderFactory(Map config) { + return null; + } + + @Override + public JsonParser createParser(Reader reader) { + return null; + } + + @Override + public JsonParser createParser(InputStream in) { + return null; + } + + @Override + public JsonParserFactory createParserFactory(Map config) { + return null; + } + + +} diff --git a/demos/customprovider-jdk9/src/main/jdk9/module-info.java b/demos/customprovider-jdk9/src/main/jdk9/module-info.java new file mode 100644 index 00000000..a77a7eb5 --- /dev/null +++ b/demos/customprovider-jdk9/src/main/jdk9/module-info.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0, which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +module org.glassfish.java.json.demos.customprovider { + requires transitive java.json; + exports org.glassfish.json.customprovider; + provides javax.json.spi.JsonProvider with org.glassfish.json.customprovider.TestProvider; +} diff --git a/demos/customprovider-jdk9/src/test/java/customprovider/test/TestProviderTest.java b/demos/customprovider-jdk9/src/test/java/customprovider/test/TestProviderTest.java new file mode 100644 index 00000000..d36b2515 --- /dev/null +++ b/demos/customprovider-jdk9/src/test/java/customprovider/test/TestProviderTest.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0, which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package customprovider.test; + +import javax.json.Json; +import javax.json.stream.JsonGenerator; +import org.glassfish.json.customprovider.TestGenerator; +import org.junit.Assert; +import org.junit.Test; + +/** + * + * @author lukas + */ +public class TestProviderTest { + + @Test + public void hello() { + try (JsonGenerator generator = Json.createGenerator(System.out)) { + Assert.assertTrue("TestGenerator is not picked up", generator instanceof TestGenerator); + generator.writeStartArray().writeEnd(); + } + System.out.println(); + System.out.println("Hurray!!!"); + } +} diff --git a/demos/facebook/pom.xml b/demos/facebook/pom.xml new file mode 100644 index 00000000..483c3748 --- /dev/null +++ b/demos/facebook/pom.xml @@ -0,0 +1,151 @@ + + + + 4.0.0 + + + org.glassfish.jsonp + demos + 1.2-SNAPSHOT + ../pom.xml + + + jar + http://maven.apache.org + jsondemos-facebook + + + org.glassfish.jsondemos.facebook.FacebookObjectSearch + + + + + all + + true + + + + javax.json + javax.json-api + compile + + + org.glassfish + javax.json + runtime + + + + + + org.codehaus.mojo + exec-maven-plugin + + + example-facebook-jdk8 + + java + + + + + ${main.class} + + + + + + + + jdk9-setup + + 9 + + + + org.glassfish + javax.json + compile + + + + ${project.build.directory}/modules + + + + + org.apache.maven.plugins + maven-dependency-plugin + + true + ${modules.directory} + + + + get-dependencies + package + + copy-dependencies + + + javax.json + + + + get-project-artifact + package + + copy + + + + + ${project.groupId} + ${project.artifactId} + ${project.version} + + + + + + + + org.codehaus.mojo + exec-maven-plugin + + java + + --add-modules + org.glassfish.java.json + --module-path + ${modules.directory} + -m + org.glassfish.java.json.demos.facebook/${main.class} + + + + + example-facebook-jdk9 + + exec + + + + + + + + + + diff --git a/demos/facebook/src/main/java/org/glassfish/jsondemos/facebook/FacebookObjectSearch.java b/demos/facebook/src/main/java/org/glassfish/jsondemos/facebook/FacebookObjectSearch.java new file mode 100644 index 00000000..4388fcb5 --- /dev/null +++ b/demos/facebook/src/main/java/org/glassfish/jsondemos/facebook/FacebookObjectSearch.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0, which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.glassfish.jsondemos.facebook; + +import javax.json.*; +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Properties; + +/** + * Parses JSON from facebook graph API using object model API. + * JSON would like : + * + * { + * data: [ + * { "from" : { "name" : "xxx", ... }, "message: "yyy", ... }, + * { "from" : { "name" : "ppp", ... }, "message: "qqq", ... }, + * ... + * ], + * ... + * } + * + * This codes writes the facebook posts to output as follows: + * xxx: yyy + * -------- + * ppp: qqq + * -------- + * + * @author Jitendra Kotamraju + */ +public class FacebookObjectSearch { + + public static void main(String... args) throws Exception { + try (InputStream is = getSearchStream(); + JsonReader rdr = Json.createReader(is)) { + + JsonObject obj = rdr.readObject(); + JsonArray results = obj.getJsonArray("data"); + for (JsonObject result : results.getValuesAs(JsonObject.class)) { + JsonValue value = result.get("from"); + if (value != null && value instanceof JsonObject) { + System.out.print(((JsonObject)value).getString("name", "anon")); + } + System.out.print(": "); + System.out.println(result.getString("message", "")); + System.out.println("-----------"); + } + } + } + + static InputStream getSearchStream() throws Exception { + Properties config = new Properties(); + config.load(FacebookObjectSearch.class.getResourceAsStream( + "/facebookconfig.properties")); + final String accessToken = (String)config.get("access_token"); + + // Gets the search stream + String searchUrl = "https://graph.facebook.com/search?q=tamil&type=post&access_token="; + URL url = new URL(searchUrl+accessToken); + HttpURLConnection con = (HttpURLConnection)url.openConnection(); + return con.getInputStream(); + } + +} diff --git a/demos/facebook/src/main/java/org/glassfish/jsondemos/facebook/FacebookStreamSearch.java b/demos/facebook/src/main/java/org/glassfish/jsondemos/facebook/FacebookStreamSearch.java new file mode 100644 index 00000000..e104cb0f --- /dev/null +++ b/demos/facebook/src/main/java/org/glassfish/jsondemos/facebook/FacebookStreamSearch.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0, which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.glassfish.jsondemos.facebook; + +import javax.json.*; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; +import java.io.*; + +/** + * Parses JSON from facebook graph API using streaming API. + * JSON would like : + * + * { + * data: [ + * { "from" : { "name" : "xxx", ... }, "message: "yyy", ... }, + * { "from" : { "name" : "ppp", ... }, "message: "qqq", ... }, + * ... + * ], + * ... + * } + * + * This codes writes the facebook posts to output as follows: + * xxx: yyy + * -------- + * ppp: qqq + * -------- + * + * @author Jitendra Kotamraju + */ +public class FacebookStreamSearch { + + public static void main(String... args) throws Exception { + try (InputStream is = FacebookObjectSearch.getSearchStream(); + JsonParser parser = Json.createParser(is)) { + while (parser.hasNext()) { + Event e = parser.next(); + if (e == Event.KEY_NAME) { + switch (parser.getString()) { + case "name": + parser.next(); + System.out.print(parser.getString()); + System.out.print(": "); + break; + case "message": + parser.next(); + System.out.println(parser.getString()); + System.out.println("---------"); + break; + } + } + } + } + } + +} diff --git a/demos/facebook/src/main/jdk9/module-info.java b/demos/facebook/src/main/jdk9/module-info.java new file mode 100644 index 00000000..5c6ca79b --- /dev/null +++ b/demos/facebook/src/main/jdk9/module-info.java @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2016, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0, which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +module org.glassfish.java.json.demos.facebook { + requires org.glassfish.java.json; +} diff --git a/demos/facebook/src/main/resources/facebookconfig.properties b/demos/facebook/src/main/resources/facebookconfig.properties new file mode 100644 index 00000000..21135453 --- /dev/null +++ b/demos/facebook/src/main/resources/facebookconfig.properties @@ -0,0 +1,11 @@ +# +# Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. +# +# This program and the accompanying materials are made available under the +# terms of the Eclipse Distribution License v. 1.0, which is available at +# http://www.eclipse.org/org/documents/edl-v10.php. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +access_token= diff --git a/demos/jaxrs/pom.xml b/demos/jaxrs/pom.xml new file mode 100644 index 00000000..c2279fc8 --- /dev/null +++ b/demos/jaxrs/pom.xml @@ -0,0 +1,59 @@ + + + + 4.0.0 + + + org.glassfish.jsonp + demos + 1.2-SNAPSHOT + ../pom.xml + + + war + http://maven.apache.org + jsondemos-jaxrs + + jsondemos-jaxrs + + + + javax.ws.rs + javax.ws.rs-api + provided + + + javax + javaee-web-api + provided + + + javax.json + javax.json-api + provided + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-war-plugin + + + jsondemos-jaxrs + + diff --git a/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/ArrayResource.java b/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/ArrayResource.java new file mode 100644 index 00000000..0a7ff5d6 --- /dev/null +++ b/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/ArrayResource.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0, which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.glassfish.jsondemos.jaxrs; + +import javax.json.*; +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; + +/** + * JsonArray as parameter and return type for a JAX-RS resource + * + * @author Jitendra Kotamraju + */ +@Path("/array") +public class ArrayResource { + private static final JsonBuilderFactory bf = Json.createBuilderFactory(null); + + @GET + @Produces(MediaType.APPLICATION_JSON) + public JsonArray doGet() { + return bf.createArrayBuilder() + .add(bf.createObjectBuilder() + .add("type", "home") + .add("number", "212 555-1234")) + .add(bf.createObjectBuilder() + .add("type", "fax") + .add("number", "646 555-4567")) + .build(); + } + + @POST + @Consumes(MediaType.APPLICATION_JSON) + public void doPost(JsonArray structure) { + System.out.println(structure); + } + +} diff --git a/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/DemoApplication.java b/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/DemoApplication.java new file mode 100644 index 00000000..af7b5ba9 --- /dev/null +++ b/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/DemoApplication.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0, which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.glassfish.jsondemos.jaxrs; + +import javax.json.stream.JsonGenerator; +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.core.Application; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * A JAX-RS Demo Application using JSON API + * + * @author Jitendra Kotamraju + */ +@ApplicationPath("/") +public class DemoApplication extends Application { + + @Override + public Set> getClasses() { + Set> set = new HashSet<>(); + set.add(ParserResource.class); + set.add(GeneratorResource.class); + set.add(ObjectResource.class); + set.add(ArrayResource.class); + set.add(StructureResource.class); + + return set; + } + + @Override + public Map getProperties() { + return new HashMap() {{ + put(JsonGenerator.PRETTY_PRINTING, true); + }}; + } +} diff --git a/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/GeneratorResource.java b/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/GeneratorResource.java new file mode 100644 index 00000000..8be73d29 --- /dev/null +++ b/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/GeneratorResource.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0, which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.glassfish.jsondemos.jaxrs; + +import javax.json.Json; +import javax.json.stream.JsonGenerator; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.StreamingOutput; +import java.io.OutputStream; + +/** + * Writes wiki's JSON example in a streaming fashion using JsonGenerator + * + * @author Jitendra Kotamraju + */ +@Path("/generator") +public class GeneratorResource { + + @GET + @Produces(MediaType.APPLICATION_JSON) + public StreamingOutput doGet() { + return new StreamingOutput() { + public void write(OutputStream os) { + writeWikiExample(os); + } + }; + } + + // Writes wiki example JSON in a streaming fashion + private void writeWikiExample(OutputStream os) { + try(JsonGenerator gene = Json.createGenerator(os)) { + gene.writeStartObject() + .write("firstName", "John") + .write("lastName", "Smith") + .write("age", 25) + .writeStartObject("address") + .write("streetAddress", "21 2nd Street") + .write("city", "New York") + .write("state", "NY") + .write("postalCode", "10021") + .writeEnd() + .writeStartArray("phoneNumber") + .writeStartObject() + .write("type", "home") + .write("number", "212 555-1234") + .writeEnd() + .writeStartObject() + .write("type", "fax") + .write("number", "646 555-4567") + .writeEnd() + .writeEnd() + .writeEnd(); + } + } + +} diff --git a/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/ObjectResource.java b/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/ObjectResource.java new file mode 100644 index 00000000..3a4e0427 --- /dev/null +++ b/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/ObjectResource.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0, which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.glassfish.jsondemos.jaxrs; + +import javax.json.*; +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; + +/** + * JsonObject as parameter and return type for a JAX-RS resource + * Writes a person's representation as JSON using JsonObject + * + * @author Jitendra Kotamraju + */ +@Path("/object") +public class ObjectResource { + private static final JsonBuilderFactory bf = Json.createBuilderFactory(null); + + @GET + @Produces(MediaType.APPLICATION_JSON) + public JsonObject doGet() { + return bf.createObjectBuilder() + .add("firstName", "John") + .add("lastName", "Smith") + .add("age", 25) + .add("address", bf.createObjectBuilder() + .add("streetAddress", "21 2nd Street") + .add("city", "New York") + .add("state", "NY") + .add("postalCode", "10021")) + .add("phoneNumber", bf.createArrayBuilder() + .add(bf.createObjectBuilder() + .add("type", "home") + .add("number", "212 555-1234")) + .add(bf.createObjectBuilder() + .add("type", "fax") + .add("number", "646 555-4567"))) + .build(); + } + + @POST + @Consumes(MediaType.APPLICATION_JSON) + public void doPost(JsonObject structure) { + System.out.println(structure); + } + +} diff --git a/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/ParserResource.java b/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/ParserResource.java new file mode 100644 index 00000000..886be9a5 --- /dev/null +++ b/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/ParserResource.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0, which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.glassfish.jsondemos.jaxrs; + +import javax.json.Json; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.StreamingOutput; +import java.io.*; +import java.net.URL; + +/** + * Filters JSON from flicker photo search REST API + * + * { + * photos : { + * photo: [ + * { id: "9889087315", secret: "40aeb70c83", server: "3818",farm: 4, ..}, + * { id: "9889087315", secret: "40aeb70c83", server: "3818",farm: 4, ..} + * ... + * ], + * ... + * } + * } + * + * @author Jitendra Kotamraju + */ +@Path("/parser") +public class ParserResource { + + @GET + @Produces("text/html") + public StreamingOutput doGet() { + return new StreamingOutput() { + public void write(OutputStream os) throws IOException { + writeFlickerFeed(os); + } + }; + } + + private void writeFlickerFeed(OutputStream os) throws IOException { + URL url = new URL("http://api.flickr.com/services/rest/?method=flickr.photos.getRecent&api_key=221160312e1c22ec60ecf336951b0e77&format=json&nojsoncallback=1&per_page=20"); + try(InputStream is = url.openStream(); + JsonParser parser = Json.createParser(is); + PrintWriter ps = new PrintWriter(new OutputStreamWriter(os, "UTF-8"))) { + String id = null; + String server = null; + String secret = null; + + ps.println(""); + while(parser.hasNext()) { + Event e = parser.next(); + if (e == Event.KEY_NAME) { + String str = parser.getString(); + switch (str) { + case "id" : + parser.next(); + id = parser.getString(); + break; + case "farm" : + parser.next(); + String farm = parser.getString(); + ps.println(""); + break; + case "server" : + parser.next(); + server = parser.getString(); + break; + case "secret" : + parser.next(); + secret = parser.getString(); + break; + } + } + } + ps.println(""); + ps.flush(); + } + } + +} diff --git a/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/StructureResource.java b/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/StructureResource.java new file mode 100644 index 00000000..1c8e786d --- /dev/null +++ b/demos/jaxrs/src/main/java/org/glassfish/jsondemos/jaxrs/StructureResource.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0, which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.glassfish.jsondemos.jaxrs; + +import javax.json.*; +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; + +/** + * JsonStructure as parameter and return type for a JAX-RS resource + * + * @author Jitendra Kotamraju + */ +@Path("/structure") +public class StructureResource { + private static final JsonBuilderFactory bf = Json.createBuilderFactory(null); + + @GET + @Produces(MediaType.APPLICATION_JSON) + public JsonStructure doGet() { + return bf.createObjectBuilder() + .add("firstName", "John") + .add("lastName", "Smith") + .add("age", 25) + .add("address", bf.createObjectBuilder() + .add("streetAddress", "21 2nd Street") + .add("city", "New York") + .add("state", "NY") + .add("postalCode", "10021")) + .add("phoneNumber", bf.createArrayBuilder() + .add(bf.createObjectBuilder() + .add("type", "home") + .add("number", "212 555-1234")) + .add(bf.createObjectBuilder() + .add("type", "fax") + .add("number", "646 555-4567"))) + .build(); + } + + @POST + @Consumes(MediaType.APPLICATION_JSON) + public void doPost(JsonStructure structure) { + System.out.println(structure); + } + +} diff --git a/demos/jsonpointer/pom.xml b/demos/jsonpointer/pom.xml new file mode 100644 index 00000000..e34a1db9 --- /dev/null +++ b/demos/jsonpointer/pom.xml @@ -0,0 +1,150 @@ + + + + 4.0.0 + + + org.glassfish.jsonp + demos + 1.2-SNAPSHOT + ../pom.xml + + + jar + http://maven.apache.org + jsondemos-jsonpointer + + + org.glassfish.jsondemos.jsonpointer.JsonpointerDemo + + + + + all + + true + + + + javax.json + javax.json-api + compile + + + org.glassfish + javax.json + runtime + + + + + + org.codehaus.mojo + exec-maven-plugin + + + example-jsonpointer-jdk8 + + java + + + + + ${main.class} + + + + + + + + jdk9-setup + + 9 + + + + org.glassfish + javax.json + compile + + + + ${project.build.directory}/modules + + + + + org.apache.maven.plugins + maven-dependency-plugin + + true + ${modules.directory} + + + + get-dependencies + package + + copy-dependencies + + + javax.json + + + + get-project-artifact + package + + copy + + + + + ${project.groupId} + ${project.artifactId} + ${project.version} + + + + + + + + org.codehaus.mojo + exec-maven-plugin + + java + + --add-modules + org.glassfish.java.json + --module-path + ${modules.directory} + -m + org.glassfish.java.json.demos.jsonpointer/${main.class} + + + + + example-jsonpointer-jdk9 + + exec + + + + + + + + + diff --git a/demos/jsonpointer/src/main/java/org/glassfish/jsondemos/jsonpointer/JsonpointerDemo.java b/demos/jsonpointer/src/main/java/org/glassfish/jsondemos/jsonpointer/JsonpointerDemo.java new file mode 100644 index 00000000..781a6c7d --- /dev/null +++ b/demos/jsonpointer/src/main/java/org/glassfish/jsondemos/jsonpointer/JsonpointerDemo.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0, which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.glassfish.jsondemos.jsonpointer; + +import javax.json.*; +import java.io.*; + +/** + * JsonPointer (http://tools.ietf.org/html/rfc6901) demo with object model API + * + * @author Jitendra Kotamraju + */ +public class JsonpointerDemo { + + public static void main(String... args) throws Exception { + testWiki(); + testPointer(); + } + + private static void testWiki() throws IOException { + try (InputStream is = JsonpointerDemo.class.getResourceAsStream("/wiki.json"); + JsonReader rdr = Json.createReader(is)) { + JsonObject person = rdr.readObject(); + + assertEquals("NY", getString(person, "/address/state")); + assertEquals("212 555-1234", getString(person, "/phoneNumber/0/number")); + } + } + + private static void testPointer() throws IOException { + try (InputStream is = JsonpointerDemo.class.getResourceAsStream("/jsonpointer.json"); + JsonReader rdr = Json.createReader(is)) { + JsonObject root = rdr.readObject(); + + assertEquals(root, get(root, "")); + assertEquals(root.get("foo"), get(root, "/foo")); + assertEquals(root.getJsonArray("foo").get(0), get(root, "/foo/0")); + assertEquals(root.get(""), get(root, "/")); + assertEquals(root.get("a/b"), get(root, "/a~1b")); + assertEquals(root.get("c%d"), get(root, "/c%d")); + assertEquals(root.get("e^f"), get(root, "/e^f")); + assertEquals(root.get("k\"l"), get(root, "/k\"l")); + assertEquals(root.get("i\\j"), get(root, "/i\\j")); + assertEquals(root.get(" "), get(root, "/ ")); + assertEquals(root.get("m~n"), get(root, "/m~0n")); + + // Adding a parent to current root and try with it + JsonObject doc = Json.createObjectBuilder().add("doc", root).build(); + root = doc.getJsonObject("doc"); + assertEquals(doc, get(doc, "")); + assertEquals(root.get("foo"), get(doc, "/doc/foo")); + assertEquals(root.getJsonArray("foo").get(0), get(doc, "/doc/foo/0")); + assertEquals(root.get(""), get(doc, "/doc/")); + assertEquals(root.get("a/b"), get(doc, "/doc/a~1b")); + assertEquals(root.get("c%d"), get(doc, "/doc/c%d")); + assertEquals(root.get("e^f"), get(doc, "/doc/e^f")); + assertEquals(root.get("k\"l"), get(doc, "/doc/k\"l")); + assertEquals(root.get("i\\j"), get(doc, "/doc/i\\j")); + assertEquals(root.get(" "), get(doc, "/doc/ ")); + assertEquals(root.get("m~n"), get(doc, "/doc/m~0n")); + } + } + + private static String getString(JsonValue root, String pointer) { + return ((JsonString)get(root, pointer)).getString(); + } + + private static JsonValue get(JsonValue root, String pointer) { + if (pointer.isEmpty()) { + return root; + } + if (pointer.charAt(0) != '/') { + throw new IllegalArgumentException( + "JsonPointer "+pointer+" doesn't start with /"); + } + + StringBuilder referenceToken = new StringBuilder(); + for(int i=1; i < pointer.length(); i++) { // 1 to skip first / + char ch = pointer.charAt(i); + if (ch == '/') { + return get(newRoot(root, referenceToken.toString()), pointer.substring(i)); + } else if (ch == '~') { + // handle escaping ~0, ~1 + if (i+1 == pointer.length()) { + throw new IllegalArgumentException("Illegal escaping: expected ~0 or ~1, but got only ~ in pointer="+pointer); + } + ch = pointer.charAt(++i); + if (ch == '0') { + referenceToken.append('~'); + } else if (ch == '1') { + referenceToken.append('/'); + } else { + throw new IllegalArgumentException("Illegal escaping: expected ~0 or ~1, but got ~"+ch+" in pointer="+pointer); + } + } else { + referenceToken.append(ch); + } + } + return newRoot(root, referenceToken.toString()); + } + + private static JsonValue newRoot(JsonValue root, String referenceToken) { + if (root instanceof JsonObject) { + return ((JsonObject)root).get(referenceToken); + } else if (root instanceof JsonArray) { + return ((JsonArray)root).get(Integer.parseInt(referenceToken)); + } + throw new IllegalArgumentException("Illegal reference token="+referenceToken+" for value="+root); + } + + private static void assertEquals(JsonValue exp, JsonValue got) { + if (exp != got) { + throw new RuntimeException("Expected = "+exp+" but got = "+got); + } + } + + private static void assertEquals(String exp, String got) { + if (!exp.equals(got)) { + throw new RuntimeException("Expected = "+exp+" but got = "+got); + } + } + +} diff --git a/demos/jsonpointer/src/main/jdk9/module-info.java b/demos/jsonpointer/src/main/jdk9/module-info.java new file mode 100644 index 00000000..ad5d0fe2 --- /dev/null +++ b/demos/jsonpointer/src/main/jdk9/module-info.java @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2016, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0, which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +module org.glassfish.java.json.demos.jsonpointer { + requires org.glassfish.java.json; +} diff --git a/demos/jsonpointer/src/main/resources/jsonpointer.json b/demos/jsonpointer/src/main/resources/jsonpointer.json new file mode 100644 index 00000000..937a098c --- /dev/null +++ b/demos/jsonpointer/src/main/resources/jsonpointer.json @@ -0,0 +1,12 @@ +{ + "foo": ["bar", "baz"], + "": 0, + "a/b": 1, + "c%d": 2, + "e^f": 3, + "g|h": 4, + "i\\j": 5, + "k\"l": 6, + " ": 7, + "m~n": 8 +} diff --git a/demos/jsonpointer/src/main/resources/wiki.json b/demos/jsonpointer/src/main/resources/wiki.json new file mode 100644 index 00000000..17bb1932 --- /dev/null +++ b/demos/jsonpointer/src/main/resources/wiki.json @@ -0,0 +1,21 @@ +{ + "firstName": "John", + "lastName": "Smith", + "age": 25, + "address": { + "streetAddress": "21 2nd Street", + "city": "New York", + "state": "NY", + "postalCode": "10021" + }, + "phoneNumber": [ + { + "type": "home", + "number": "212 555-1234" + }, + { + "type": "fax", + "number": "646 555-4567" + } + ] +} diff --git a/demos/pom.xml b/demos/pom.xml new file mode 100644 index 00000000..74c9d931 --- /dev/null +++ b/demos/pom.xml @@ -0,0 +1,69 @@ + + + + 4.0.0 + + + org.glassfish + json + 1.2-SNAPSHOT + ../pom.xml + + + pom + http://maven.apache.org + org.glassfish.jsonp + demos + + + + all + + true + + + + javax + javaee-web-api + provided + + + javax.json + javax.json-api + provided + + + + jaxrs + twitter + facebook + jsonpointer + servlet + + + + jdk9-all + + 9 + + + jaxrs + twitter + facebook + jsonpointer + servlet + customprovider-jdk9 + + + + diff --git a/demos/servlet/pom.xml b/demos/servlet/pom.xml new file mode 100644 index 00000000..f46ac79b --- /dev/null +++ b/demos/servlet/pom.xml @@ -0,0 +1,54 @@ + + + + 4.0.0 + + + org.glassfish.jsonp + demos + 1.2-SNAPSHOT + ../pom.xml + + + war + http://maven.apache.org + jsondemos-servlet + + jsondemos-servlet + + + + javax + javaee-web-api + provided + + + javax.json + javax.json-api + provided + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-war-plugin + + + jsondemos-servlet + + diff --git a/demos/servlet/src/main/java/org/glassfish/jsondemos/servlet/ArrayServlet.java b/demos/servlet/src/main/java/org/glassfish/jsondemos/servlet/ArrayServlet.java new file mode 100644 index 00000000..70d8bbc0 --- /dev/null +++ b/demos/servlet/src/main/java/org/glassfish/jsondemos/servlet/ArrayServlet.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0, which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.glassfish.jsondemos.servlet; + +import javax.json.*; +import javax.servlet.*; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.*; +import java.io.IOException; + +/** + * Writes a JsonArray using HttpServletResponse#getWriter + * http://localhost:8080/jsondemos-servlet/array + * + * Writes a JsonArray using HttpServletResponse#getOutputStream + * http://localhost:8080/jsondemos-servlet/array?stream + * + * + * @author Jitendra Kotamraju + */ +@WebServlet("/array") +public class ArrayServlet extends HttpServlet { + private static final JsonBuilderFactory bf = Json.createBuilderFactory(null); + private static final JsonWriterFactory wf = Json.createWriterFactory(null); + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException { + JsonArray array = bf.createArrayBuilder() + .add(bf.createObjectBuilder() + .add("type", "home") + .add("number", "212 555-1234")) + .add(bf.createObjectBuilder() + .add("type", "fax") + .add("number", "646 555-4567")) + .build(); + res.setStatus(HttpServletResponse.SC_OK); + res.setContentType("application/json"); + res.setCharacterEncoding("UTF-8"); + + String q = req.getQueryString(); + boolean isStream = q != null && q.equals("stream"); + JsonWriter writer = isStream + ? wf.createWriter(res.getOutputStream()) + : wf.createWriter(res.getWriter()); + writer.write(array); + // not closing writer intentionally + } + +} diff --git a/demos/twitter/pom.xml b/demos/twitter/pom.xml new file mode 100644 index 00000000..8c56cda9 --- /dev/null +++ b/demos/twitter/pom.xml @@ -0,0 +1,154 @@ + + + + 4.0.0 + + + org.glassfish.jsonp + demos + 1.2-SNAPSHOT + ../pom.xml + + + jar + http://maven.apache.org + jsondemos-twitter + + + org.glassfish.jsondemos.twitter.TwitterObjectSearch + + + + + all + + true + + + + + org.codehaus.mojo + exec-maven-plugin + + + example-twitter-jdk8 + + java + + + + + ${main.class} + + + + + + + + jdk9-setup + + 9 + + + + org.glassfish + javax.json + compile + + + + ${project.build.directory}/modules + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + default-compile + + + --add-modules + java.xml.bind + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + true + ${modules.directory} + + + + get-dependencies + package + + copy-dependencies + + + javax.json + + + + get-project-artifact + package + + copy + + + + + ${project.groupId} + ${project.artifactId} + ${project.version} + + + + + + + + org.codehaus.mojo + exec-maven-plugin + + java + + --add-modules + java.xml.bind,org.glassfish.java.json + --module-path + ${modules.directory} + -m + org.glassfish.java.json.demos.twitter/${main.class} + + + + + example-twitter-jdk9 + + exec + + + + + + + + + + diff --git a/demos/twitter/src/main/java/org/glassfish/jsondemos/twitter/TwitterObjectSearch.java b/demos/twitter/src/main/java/org/glassfish/jsondemos/twitter/TwitterObjectSearch.java new file mode 100644 index 00000000..87b6d3fc --- /dev/null +++ b/demos/twitter/src/main/java/org/glassfish/jsondemos/twitter/TwitterObjectSearch.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0, which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.glassfish.jsondemos.twitter; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import javax.json.*; +import javax.xml.bind.DatatypeConverter; +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.util.*; + +/** + * Parses JSON from twitter search REST API using object model API. + * JSON would like : + * + * { + " statuses": [ + * { ..., "user" : { "name" : "xxx", ...}, "text: "yyy", ... }, + * { ..., "user" : { "name" : "ppp", ...}, "text: "qqq", ... }, + * ... + * ], + * ... + * } + * + * This codes writes the tweets to output as follows: + * xxx: yyy + * -------- + * ppp: qqq + * -------- + * + * @author Jitendra Kotamraju + */ +public class TwitterObjectSearch { + + public static void main(String... args) throws Exception { + try (InputStream is = getSearchStream(); + JsonReader rdr = Json.createReader(is)) { + + JsonObject obj = rdr.readObject(); + JsonArray results = obj.getJsonArray("statuses"); + for (JsonObject result : results.getValuesAs(JsonObject.class)) { + System.out.print(result.getJsonObject("user").getString("name", "anonymous")); + System.out.print(": "); + System.out.println(result.get("text")); + System.out.println("-----------"); + } + +// All the tweets are collected into Stream and printed +// obj.getJsonArray("statuses").getValuesAs(JsonObject.class) +// .stream() +// .map(v -> v.getString("text")) +// .forEach(s -> { System.out.println(s); } ); + } + } + + static InputStream getSearchStream() throws Exception { + final String searchStr = "#javaone"; + String searchUrl = "https://api.twitter.com/1.1/search/tweets.json"; + + Properties config = new Properties(); + config.load(TwitterObjectSearch.class.getResourceAsStream( + "/twitterconfig.properties")); + + final String consumerKey = (String)config.get("consumer-key"); + final String consumerSecret = (String)config.get("consumer-secret"); + final String accessToken = (String)config.get("access-token"); + final String accessTokenSecret = (String)config.get("access-token-secret"); + final int timestamp = (int)(System.currentTimeMillis()/1000); + + Map map = new TreeMap() {{ + put("count", "100"); + put("oauth_consumer_key", consumerKey); + put("oauth_nonce", "4b25256957d75b6370f33a4501dc5e7e"); // TODO + put("oauth_signature_method", "HMAC-SHA1"); + put("oauth_timestamp", ""+timestamp); + put("oauth_token", accessToken); + put("oauth_version", "1.0"); + put("q", searchStr); + }}; + + // Builds param string + StringBuilder paramsBuilder = new StringBuilder(); + boolean first = true; + for(Map.Entry e : map.entrySet()) { + if (!first) { + paramsBuilder.append('&'); + } + first = false; + paramsBuilder.append(e.getKey()); + paramsBuilder.append("="); + paramsBuilder.append(URLEncoder.encode(e.getValue(), "UTF-8")); + } + String paramsString = paramsBuilder.toString(); + + // builds signature string + StringBuilder signatureBuilder = new StringBuilder(); + signatureBuilder.append("GET"); + signatureBuilder.append('&'); + signatureBuilder.append(URLEncoder.encode(searchUrl, "UTF-8")); + signatureBuilder.append('&'); + signatureBuilder.append(URLEncoder.encode(paramsString, "UTF-8")); + String signatureBasedString = signatureBuilder.toString(); + + // Create authorization signature + Mac m = Mac.getInstance("HmacSHA1"); + m.init(new SecretKeySpec((consumerSecret+"&"+accessTokenSecret).getBytes(), "HmacSHA1")); + m.update(signatureBasedString.getBytes()); + byte[] res = m.doFinal(); + final String oauthSig = URLEncoder.encode(DatatypeConverter.printBase64Binary(res), "UTF-8"); + map.put("oauth_signature", oauthSig); + map.remove("count"); + map.remove("q"); + + // Build Authorization header + StringBuilder authorizationBuilder = new StringBuilder(); + authorizationBuilder.append("OAuth "); + first = true; + for(Map.Entry e : map.entrySet()) { + if (!first) { + authorizationBuilder.append(','); + authorizationBuilder.append(' '); + } + first = false; + authorizationBuilder.append(e.getKey()); + authorizationBuilder.append('='); + authorizationBuilder.append('"'); + authorizationBuilder.append(e.getValue()); + authorizationBuilder.append('"'); + } + + // Gets the search stream + URL url = new URL(searchUrl+"?q="+URLEncoder.encode(searchStr, "UTF-8")+ + "&count=100"); + HttpURLConnection con = (HttpURLConnection)url.openConnection(); + con.addRequestProperty("Authorization", authorizationBuilder.toString()); + return con.getInputStream(); + } + +} diff --git a/demos/twitter/src/main/java/org/glassfish/jsondemos/twitter/TwitterStreamSearch.java b/demos/twitter/src/main/java/org/glassfish/jsondemos/twitter/TwitterStreamSearch.java new file mode 100644 index 00000000..ba16f543 --- /dev/null +++ b/demos/twitter/src/main/java/org/glassfish/jsondemos/twitter/TwitterStreamSearch.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0, which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.glassfish.jsondemos.twitter; + +import javax.json.*; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; +import java.io.*; + +/** + * Parses JSON from twitter search REST API using streaming API. + * JSON would like : + * + * { + " statuses": [ + * { ..., "user" : { "name" : "xxx", ...}, "text: "yyy", ... }, + * { ..., "user" : { "name" : "ppp", ...}, "text: "qqq", ... }, + * ... + * ], + * ... + * } + * + * This codes writes the tweets to output as follows: + * xxx: yyy + * -------- + * ppp: qqq + * -------- + * + * TODO need to do better, also the last tweet is repeated ! + * + * @author Jitendra Kotamraju + */ +public class TwitterStreamSearch { + + public static void main(String... args) throws Exception { + try (InputStream is = TwitterObjectSearch.getSearchStream(); + JsonParser parser = Json.createParser(is)) { + int depth = 0; + String name = null; + String text = null; + while (parser.hasNext()) { + Event e = parser.next(); + if (e == Event.KEY_NAME) { + switch (parser.getString()) { + case "name": + if (depth == 3) { + parser.next(); + name = parser.getString(); + } + break; + case "text": + if (depth == 2) { + parser.next(); + text = parser.getString(); + } + break; + } + } else if (e == Event.START_OBJECT) { + ++depth; + } else if (e == Event.END_OBJECT) { + --depth; + if (depth == 1) { + System.out.println(name+": "+text); + System.out.println("-----------"); + + } + } + } + } + } + +} diff --git a/demos/twitter/src/main/jdk9/module-info.java b/demos/twitter/src/main/jdk9/module-info.java new file mode 100644 index 00000000..cc1cd567 --- /dev/null +++ b/demos/twitter/src/main/jdk9/module-info.java @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2016, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0, which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +module org.glassfish.java.json.demos.twitter { + requires java.xml.bind; + requires org.glassfish.java.json; +} diff --git a/demos/twitter/src/main/resources/twitterconfig.properties b/demos/twitter/src/main/resources/twitterconfig.properties new file mode 100644 index 00000000..45a42552 --- /dev/null +++ b/demos/twitter/src/main/resources/twitterconfig.properties @@ -0,0 +1,15 @@ +# +# Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. +# +# This program and the accompanying materials are made available under the +# terms of the Eclipse Distribution License v. 1.0, which is available at +# http://www.eclipse.org/org/documents/edl-v10.php. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +consumer-key= +consumer-secret= +access-token= +access-token-secret= + diff --git a/gf/customprovider/pom.xml b/gf/customprovider/pom.xml new file mode 100644 index 00000000..52ff6494 --- /dev/null +++ b/gf/customprovider/pom.xml @@ -0,0 +1,38 @@ + + + + 4.0.0 + + + org.glassfish.jsonp + providers + 1.2-SNAPSHOT + ../pom.xml + + + war + http://maven.apache.org + customprovider + + customprovider + + + customprovider + + diff --git a/gf/customprovider/src/main/java/org/glassfish/json/customprovider/TestGenerator.java b/gf/customprovider/src/main/java/org/glassfish/json/customprovider/TestGenerator.java new file mode 100644 index 00000000..117fa270 --- /dev/null +++ b/gf/customprovider/src/main/java/org/glassfish/json/customprovider/TestGenerator.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.customprovider; + +import javax.json.JsonException; +import javax.json.JsonValue; +import javax.json.stream.JsonGenerator; +import java.io.IOException; +import java.io.Writer; +import java.math.BigDecimal; +import java.math.BigInteger; + +/** + * @author Jitendra Kotamraju + */ +public class TestGenerator implements JsonGenerator { + private final Writer writer; + + public TestGenerator(Writer writer) { + this.writer = writer; + } + + @Override + public void flush() { + } + + @Override + public JsonGenerator writeStartObject() { + return null; + } + + @Override + public JsonGenerator writeStartObject(String name) { + return null; + } + + @Override + public JsonGenerator write(String name, String fieldValue) { + return null; + } + + @Override + public JsonGenerator write(String name, int value) { + return null; + } + + @Override + public JsonGenerator write(String name, long value) { + return null; + } + + @Override + public JsonGenerator write(String name, double value) { + return null; + } + + @Override + public JsonGenerator write(String name, BigInteger value) { + return null; + } + + @Override + public JsonGenerator write(String name, BigDecimal value) { + return null; + } + + @Override + public JsonGenerator write(String name, boolean value) { + return null; + } + + @Override + public JsonGenerator writeNull(String name) { + return null; + } + + @Override + public JsonGenerator write(JsonValue value) { + return null; + } + + @Override + public JsonGenerator writeStartArray() { + try { + writer.write("["); + } catch(IOException ioe) { + throw new JsonException("I/O error", ioe); + } + return this; + } + + @Override + public JsonGenerator writeStartArray(String name) { + return null; + } + + @Override + public JsonGenerator write(String name, JsonValue value) { + return null; + } + + public JsonGenerator write(String value) { + return null; + } + + + public JsonGenerator write(int value) { + return null; + } + + @Override + public JsonGenerator write(long value) { + return null; + } + + @Override + public JsonGenerator write(double value) { + return null; + } + + @Override + public JsonGenerator write(BigInteger value) { + return null; + } + + @Override + public JsonGenerator write(BigDecimal value) { + return null; + } + + public JsonGenerator write(boolean value) { + return null; + } + + public JsonGenerator writeNull() { + return null; + } + + @Override + public JsonGenerator writeEnd() { + try { + writer.write("]"); + } catch(IOException ioe) { + throw new JsonException("I/O error", ioe); + } + return this; + } + + public void close() { + try { + writer.close(); + } catch(IOException ioe) { + throw new JsonException("I/O error", ioe); + } + } + +} diff --git a/gf/customprovider/src/main/java/org/glassfish/json/customprovider/TestProvider.java b/gf/customprovider/src/main/java/org/glassfish/json/customprovider/TestProvider.java new file mode 100644 index 00000000..0d810f58 --- /dev/null +++ b/gf/customprovider/src/main/java/org/glassfish/json/customprovider/TestProvider.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.customprovider; + +import javax.json.*; +import javax.json.spi.JsonProvider; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonGeneratorFactory; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParserFactory; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.util.Map; + +/** + * @author Jitendra Kotamraju + */ +public class TestProvider extends JsonProvider { + + @Override + public JsonGenerator createGenerator(Writer writer) { + return new TestGenerator(writer); + } + + @Override + public JsonGenerator createGenerator(OutputStream out) { + return null; + } + + @Override + public JsonGeneratorFactory createGeneratorFactory(Map config) { + return null; + } + + @Override + public JsonReader createReader(Reader reader) { + return null; + } + + @Override + public JsonReader createReader(InputStream in) { + return null; + } + + @Override + public JsonWriter createWriter(Writer writer) { + return null; + } + + @Override + public JsonWriter createWriter(OutputStream out) { + return null; + } + + @Override + public JsonWriterFactory createWriterFactory(Map config) { + return null; + } + + @Override + public JsonReaderFactory createReaderFactory(Map config) { + return null; + } + + @Override + public JsonObjectBuilder createObjectBuilder() { + return null; + } + + @Override + public JsonArrayBuilder createArrayBuilder() { + return null; + } + + @Override + public JsonBuilderFactory createBuilderFactory(Map config) { + return null; + } + + @Override + public JsonParser createParser(Reader reader) { + return null; + } + + @Override + public JsonParser createParser(InputStream in) { + return null; + } + + @Override + public JsonParserFactory createParserFactory(Map config) { + return null; + } + + +} diff --git a/gf/customprovider/src/main/java/org/glassfish/json/customprovider/TestServlet.java b/gf/customprovider/src/main/java/org/glassfish/json/customprovider/TestServlet.java new file mode 100644 index 00000000..ef7523ba --- /dev/null +++ b/gf/customprovider/src/main/java/org/glassfish/json/customprovider/TestServlet.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.customprovider; + +import javax.servlet.annotation.*; +import javax.servlet.http.*; +import javax.servlet.*; +import java.io.IOException; +import javax.json.*; +import javax.json.stream.*; +import java.io.*; + +/** + * @author Jitendra Kotamraju + */ +@WebServlet("/json") +public class TestServlet extends HttpServlet { + + public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException { + try { + res.setStatus(200); + res.setContentType("application/json"); + OutputStream os = res.getOutputStream(); + JsonGenerator generator = Json.createGenerator(new OutputStreamWriter(os)); + if (!(generator instanceof TestGenerator)) { + throw new RuntimeException("MyGenerator is not picked up"); + } + generator.writeStartArray().writeEnd(); + generator.close(); + os.close(); + } catch(IOException ioe) { + throw new ServletException(ioe); + } + } + +} diff --git a/gf/customprovider/src/main/resources/META-INF/services/javax.json.spi.JsonProvider b/gf/customprovider/src/main/resources/META-INF/services/javax.json.spi.JsonProvider new file mode 100644 index 00000000..0647df26 --- /dev/null +++ b/gf/customprovider/src/main/resources/META-INF/services/javax.json.spi.JsonProvider @@ -0,0 +1 @@ +org.glassfish.json.customprovider.TestProvider diff --git a/gf/defaultprovider/pom.xml b/gf/defaultprovider/pom.xml new file mode 100644 index 00000000..83cf08e7 --- /dev/null +++ b/gf/defaultprovider/pom.xml @@ -0,0 +1,38 @@ + + + + 4.0.0 + + + org.glassfish.jsonp + providers + 1.2-SNAPSHOT + ../pom.xml + + + war + http://maven.apache.org + defaultprovider + + defaultprovider + + + defaultprovider + + diff --git a/gf/defaultprovider/src/main/java/org/glassfish/json/defaultprovider/TestServlet.java b/gf/defaultprovider/src/main/java/org/glassfish/json/defaultprovider/TestServlet.java new file mode 100644 index 00000000..01001cf3 --- /dev/null +++ b/gf/defaultprovider/src/main/java/org/glassfish/json/defaultprovider/TestServlet.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.defaultprovider; + +import javax.servlet.annotation.*; +import javax.servlet.http.*; +import javax.servlet.*; +import java.io.IOException; +import javax.json.*; +import javax.json.stream.*; +import java.io.*; + +/** + * @author Jitendra Kotamraju + */ +@WebServlet("/json") +public class TestServlet extends HttpServlet { + + public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException { + try { + res.setStatus(200); + res.setContentType("application/json"); + OutputStream os = res.getOutputStream(); + JsonGenerator generator = Json.createGenerator(os); + generator.writeStartArray().writeEnd(); + generator.close(); + } catch(IOException ioe) { + throw new ServletException(ioe); + } + } + +} diff --git a/gf/pom.xml b/gf/pom.xml new file mode 100644 index 00000000..1ac95111 --- /dev/null +++ b/gf/pom.xml @@ -0,0 +1,59 @@ + + + + 4.0.0 + + + org.glassfish + json + 1.2-SNAPSHOT + ../pom.xml + + + pom + http://maven.apache.org + org.glassfish.jsonp + providers + + + + javax + javaee-web-api + provided + + + javax.json + javax.json-api + provided + + + + + + org.apache.maven.plugins + maven-war-plugin + + + customprovider + + + customprovider + defaultprovider + + diff --git a/impl/pom.xml b/impl/pom.xml new file mode 100644 index 00000000..a86c5573 --- /dev/null +++ b/impl/pom.xml @@ -0,0 +1,149 @@ + + + + + 4.0.0 + + + org.glassfish + json + 1.2-SNAPSHOT + ../pom.xml + + + org.glassfish + javax.json + bundle + 1.2-SNAPSHOT + JSR 374 (JSON Processing) Default Provider + Default provider for JSR 374:Java API for Processing JSON + https://javaee.github.io/jsonp + + + org.glassfish.json + org.glassfish.json.api + + + + + + org.glassfish.build + spec-version-maven-plugin + + + ${non.final} + impl + ${spec_version} + ${new_spec_version} + ${new_spec_impl_version} + ${impl_version} + ${new_impl_version} + ${api_package} + ${impl_namespace} + + + + + + set-spec-properties + check-module + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + attach-javadocs + + jar + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + ${spec.bundle.version} + ${spec.bundle.symbolic-name} + ${spec.extension.name} + ${spec.implementation.version} + ${spec.specification.version} + ${packages.export} + ${packages.private} + + + + + + + + javax.json + javax.json-api + + + + + + jdk9-setup + + 9 + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + --add-modules + java.json + --module-path + ${project.build.directory}/../../api/target/javax.json-api-${project.version}.jar + + + + + org.apache.felix + maven-bundle-plugin + + + target/classes/module-info.class + + + + + + + + diff --git a/impl/src/main/java/org/glassfish/json/BufferPoolImpl.java b/impl/src/main/java/org/glassfish/json/BufferPoolImpl.java new file mode 100644 index 00000000..36d8635d --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/BufferPoolImpl.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import org.glassfish.json.api.BufferPool; + +import java.lang.ref.WeakReference; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * char[] pool that pool instances of char[] which are expensive to create. + * + * @author Jitendra Kotamraju + */ +class BufferPoolImpl implements BufferPool { + + // volatile since multiple threads may access queue reference + private volatile WeakReference> queue; + + /** + * Gets a new object from the pool. + * + *

+ * If no object is available in the pool, this method creates a new one. + * + * @return + * always non-null. + */ + @Override + public final char[] take() { + char[] t = getQueue().poll(); + if (t==null) + return new char[4096]; + return t; + } + + private ConcurrentLinkedQueue getQueue() { + WeakReference> q = queue; + if (q != null) { + ConcurrentLinkedQueue d = q.get(); + if (d != null) + return d; + } + + // overwrite the queue + ConcurrentLinkedQueue d = new ConcurrentLinkedQueue<>(); + queue = new WeakReference<>(d); + + return d; + } + + /** + * Returns an object back to the pool. + */ + @Override + public final void recycle(char[] t) { + getQueue().offer(t); + } + +} diff --git a/impl/src/main/java/org/glassfish/json/JsonArrayBuilderImpl.java b/impl/src/main/java/org/glassfish/json/JsonArrayBuilderImpl.java new file mode 100644 index 00000000..97a70229 --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonArrayBuilderImpl.java @@ -0,0 +1,479 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import org.glassfish.json.api.BufferPool; + +import javax.json.*; +import java.io.StringWriter; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +/** + * JsonArrayBuilder implementation + * + * @author Jitendra Kotamraju + * @author Kin-man Chung + */ + +class JsonArrayBuilderImpl implements JsonArrayBuilder { + private ArrayList valueList; + private final BufferPool bufferPool; + + JsonArrayBuilderImpl(BufferPool bufferPool) { + this.bufferPool = bufferPool; + } + + JsonArrayBuilderImpl(JsonArray array, BufferPool bufferPool) { + this.bufferPool = bufferPool; + valueList = new ArrayList<>(); + valueList.addAll(array); + } + + JsonArrayBuilderImpl(Collection collection, BufferPool bufferPool) { + this.bufferPool = bufferPool; + valueList = new ArrayList<>(); + populate(collection); + } + + @Override + public JsonArrayBuilder add(JsonValue value) { + validateValue(value); + addValueList(value); + return this; + } + + @Override + public JsonArrayBuilder add(String value) { + validateValue(value); + addValueList(new JsonStringImpl(value)); + return this; + } + + @Override + public JsonArrayBuilder add(BigDecimal value) { + validateValue(value); + addValueList(JsonNumberImpl.getJsonNumber(value)); + return this; + } + + @Override + public JsonArrayBuilder add(BigInteger value) { + validateValue(value); + addValueList(JsonNumberImpl.getJsonNumber(value)); + return this; + } + + @Override + public JsonArrayBuilder add(int value) { + addValueList(JsonNumberImpl.getJsonNumber(value)); + return this; + } + + @Override + public JsonArrayBuilder add(long value) { + addValueList(JsonNumberImpl.getJsonNumber(value)); + return this; + } + + @Override + public JsonArrayBuilder add(double value) { + addValueList(JsonNumberImpl.getJsonNumber(value)); + return this; + } + + @Override + public JsonArrayBuilder add(boolean value) { + addValueList(value ? JsonValue.TRUE : JsonValue.FALSE); + return this; + } + + @Override + public JsonArrayBuilder addNull() { + addValueList(JsonValue.NULL); + return this; + } + + @Override + public JsonArrayBuilder add(JsonObjectBuilder builder) { + if (builder == null) { + throw new NullPointerException(JsonMessages.ARRBUILDER_OBJECT_BUILDER_NULL()); + } + addValueList(builder.build()); + return this; + } + + @Override + public JsonArrayBuilder add(JsonArrayBuilder builder) { + if (builder == null) { + throw new NullPointerException(JsonMessages.ARRBUILDER_ARRAY_BUILDER_NULL()); + } + addValueList(builder.build()); + return this; + } + + @Override + public JsonArrayBuilder addAll(JsonArrayBuilder builder) { + if (builder == null) { + throw new NullPointerException(JsonMessages.ARRBUILDER_ARRAY_BUILDER_NULL()); + } + if (valueList == null) { + valueList = new ArrayList<>(); + } + valueList.addAll(builder.build()); + return this; + } + + @Override + public JsonArrayBuilder add(int index, JsonValue value) { + validateValue(value); + addValueList(index, value); + return this; + } + + @Override + public JsonArrayBuilder add(int index, String value) { + validateValue(value); + addValueList(index, new JsonStringImpl(value)); + return this; + } + + @Override + public JsonArrayBuilder add(int index, BigDecimal value) { + validateValue(value); + addValueList(index, JsonNumberImpl.getJsonNumber(value)); + return this; + } + + @Override + public JsonArrayBuilder add(int index, BigInteger value) { + validateValue(value); + addValueList(index, JsonNumberImpl.getJsonNumber(value)); + return this; + } + + @Override + public JsonArrayBuilder add(int index, int value) { + addValueList(index, JsonNumberImpl.getJsonNumber(value)); + return this; + } + + @Override + public JsonArrayBuilder add(int index, long value) { + addValueList(index, JsonNumberImpl.getJsonNumber(value)); + return this; + } + + @Override + public JsonArrayBuilder add(int index, double value) { + addValueList(index, JsonNumberImpl.getJsonNumber(value)); + return this; + } + + @Override + public JsonArrayBuilder add(int index, boolean value) { + addValueList(index, value ? JsonValue.TRUE : JsonValue.FALSE); + return this; + } + + @Override + public JsonArrayBuilder addNull(int index) { + addValueList(index, JsonValue.NULL); + return this; + } + + @Override + public JsonArrayBuilder add(int index, JsonObjectBuilder builder) { + if (builder == null) { + throw new NullPointerException(JsonMessages.ARRBUILDER_OBJECT_BUILDER_NULL()); + } + addValueList(index, builder.build()); + return this; + } + + @Override + public JsonArrayBuilder add(int index, JsonArrayBuilder builder) { + if (builder == null) { + throw new NullPointerException(JsonMessages.ARRBUILDER_OBJECT_BUILDER_NULL()); + } + addValueList(index, builder.build()); + return this; + } + + @Override + public JsonArrayBuilder set(int index, JsonValue value) { + validateValue(value); + setValueList(index, value); + return this; + } + + @Override + public JsonArrayBuilder set(int index, String value) { + validateValue(value); + setValueList(index, new JsonStringImpl(value)); + return this; + } + + @Override + public JsonArrayBuilder set(int index, BigDecimal value) { + validateValue(value); + setValueList(index, JsonNumberImpl.getJsonNumber(value)); + return this; + } + + @Override + public JsonArrayBuilder set(int index, BigInteger value) { + validateValue(value); + setValueList(index, JsonNumberImpl.getJsonNumber(value)); + return this; + } + + @Override + public JsonArrayBuilder set(int index, int value) { + setValueList(index, JsonNumberImpl.getJsonNumber(value)); + return this; + } + + @Override + public JsonArrayBuilder set(int index, long value) { + setValueList(index, JsonNumberImpl.getJsonNumber(value)); + return this; + } + + @Override + public JsonArrayBuilder set(int index, double value) { + setValueList(index, JsonNumberImpl.getJsonNumber(value)); + return this; + } + + @Override + public JsonArrayBuilder set(int index, boolean value) { + setValueList(index, value ? JsonValue.TRUE : JsonValue.FALSE); + return this; + } + + @Override + public JsonArrayBuilder setNull(int index) { + setValueList(index, JsonValue.NULL); + return this; + } + + @Override + public JsonArrayBuilder set(int index, JsonObjectBuilder builder) { + if (builder == null) { + throw new NullPointerException(JsonMessages.ARRBUILDER_OBJECT_BUILDER_NULL()); + } + setValueList(index, builder.build()); + return this; + } + + @Override + public JsonArrayBuilder set(int index, JsonArrayBuilder builder) { + if (builder == null) { + throw new NullPointerException(JsonMessages.ARRBUILDER_OBJECT_BUILDER_NULL()); + } + setValueList(index, builder.build()); + return this; + } + + @Override + public JsonArrayBuilder remove(int index) { + if (valueList == null) { + throw new IndexOutOfBoundsException(JsonMessages.ARRBUILDER_VALUELIST_NULL(index, 0)); + } + valueList.remove(index); + return this; + } + + @Override + public JsonArray build() { + List snapshot; + if (valueList == null) { + snapshot = Collections.emptyList(); + } else { + // Should we trim to minimize storage ? + // valueList.trimToSize(); + snapshot = Collections.unmodifiableList(valueList); + } + valueList = null; + return new JsonArrayImpl(snapshot, bufferPool); + } + + private void populate(Collection collection) { + for (Object value : collection) { + if (value != null && value instanceof Optional) { + ((Optional) value).ifPresent(v -> + this.valueList.add(MapUtil.handle(v, bufferPool))); + } else { + this.valueList.add(MapUtil.handle(value, bufferPool)); + } + } + } + + private void addValueList(JsonValue value) { + if (valueList == null) { + valueList = new ArrayList<>(); + } + valueList.add(value); + } + + private void addValueList(int index, JsonValue value) { + if (valueList == null) { + valueList = new ArrayList<>(); + } + valueList.add(index, value); + } + + private void setValueList(int index, JsonValue value) { + if (valueList == null) { + throw new IndexOutOfBoundsException(JsonMessages.ARRBUILDER_VALUELIST_NULL(index, 0)); + } + valueList.set(index, value); + } + + private void validateValue(Object value) { + if (value == null) { + throw new NullPointerException(JsonMessages.ARRBUILDER_VALUE_NULL()); + } + } + + private static final class JsonArrayImpl extends AbstractList implements JsonArray { + private final List valueList; // Unmodifiable + private final BufferPool bufferPool; + + JsonArrayImpl(List valueList, BufferPool bufferPool) { + this.valueList = valueList; + this.bufferPool = bufferPool; + } + + @Override + public int size() { + return valueList.size(); + } + + @Override + public JsonObject getJsonObject(int index) { + return (JsonObject)valueList.get(index); + } + + @Override + public JsonArray getJsonArray(int index) { + return (JsonArray)valueList.get(index); + } + + @Override + public JsonNumber getJsonNumber(int index) { + return (JsonNumber)valueList.get(index); + } + + @Override + public JsonString getJsonString(int index) { + return (JsonString)valueList.get(index); + } + + @Override + @SuppressWarnings("unchecked") + public List getValuesAs(Class clazz) { + return (List)valueList; + } + + @Override + public String getString(int index) { + return getJsonString(index).getString(); + } + + @Override + public String getString(int index, String defaultValue) { + try { + return getString(index); + } catch (Exception e) { + return defaultValue; + } + } + + @Override + public int getInt(int index) { + return getJsonNumber(index).intValue(); + } + + @Override + public int getInt(int index, int defaultValue) { + try { + return getInt(index); + } catch (Exception e) { + return defaultValue; + } + } + + @Override + public boolean getBoolean(int index) { + JsonValue jsonValue = get(index); + if (jsonValue == JsonValue.TRUE) { + return true; + } else if (jsonValue == JsonValue.FALSE) { + return false; + } else { + throw new ClassCastException(); + } + } + + @Override + public boolean getBoolean(int index, boolean defaultValue) { + try { + return getBoolean(index); + } catch (Exception e) { + return defaultValue; + } + } + + @Override + public boolean isNull(int index) { + return valueList.get(index).equals(JsonValue.NULL); + } + + @Override + public ValueType getValueType() { + return ValueType.ARRAY; + } + + @Override + public JsonValue get(int index) { + return valueList.get(index); + } + + @Override + public String toString() { + StringWriter sw = new StringWriter(); + try (JsonWriter jw = new JsonWriterImpl(sw, bufferPool)) { + jw.write(this); + } + return sw.toString(); + } + + @Override + public JsonArray asJsonArray() { + return this; + } + } +} + diff --git a/impl/src/main/java/org/glassfish/json/JsonBuilderFactoryImpl.java b/impl/src/main/java/org/glassfish/json/JsonBuilderFactoryImpl.java new file mode 100644 index 00000000..ad1967e9 --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonBuilderFactoryImpl.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import java.util.Collection; +import org.glassfish.json.api.BufferPool; + +import javax.json.JsonObject; +import javax.json.JsonArray; +import javax.json.JsonArrayBuilder; +import javax.json.JsonBuilderFactory; +import javax.json.JsonObjectBuilder; +import java.util.Collections; +import java.util.Map; + +/** + * @author Jitendra Kotamraju + */ +class JsonBuilderFactoryImpl implements JsonBuilderFactory { + private final Map config; + private final BufferPool bufferPool; + + JsonBuilderFactoryImpl(BufferPool bufferPool) { + this.config = Collections.emptyMap(); + this.bufferPool = bufferPool; + } + + @Override + public JsonObjectBuilder createObjectBuilder() { + return new JsonObjectBuilderImpl(bufferPool); + } + + @Override + public JsonObjectBuilder createObjectBuilder(JsonObject object) { + return new JsonObjectBuilderImpl(object, bufferPool); + } + + @Override + public JsonObjectBuilder createObjectBuilder(Map object) { + return new JsonObjectBuilderImpl(object, bufferPool); + } + + @Override + public JsonArrayBuilder createArrayBuilder() { + return new JsonArrayBuilderImpl(bufferPool); + } + + @Override + public JsonArrayBuilder createArrayBuilder(JsonArray array) { + return new JsonArrayBuilderImpl(array, bufferPool); + } + + @Override + public JsonArrayBuilder createArrayBuilder(Collection collection) { + return new JsonArrayBuilderImpl(collection, bufferPool); + } + + @Override + public Map getConfigInUse() { + return config; + } +} diff --git a/impl/src/main/java/org/glassfish/json/JsonGeneratorFactoryImpl.java b/impl/src/main/java/org/glassfish/json/JsonGeneratorFactoryImpl.java new file mode 100644 index 00000000..c1baad86 --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonGeneratorFactoryImpl.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import org.glassfish.json.api.BufferPool; + +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonGeneratorFactory; +import java.io.OutputStream; +import java.io.Writer; +import java.nio.charset.Charset; +import java.util.Map; + +/** + * @author Jitendra Kotamraju + */ +class JsonGeneratorFactoryImpl implements JsonGeneratorFactory { + + private final boolean prettyPrinting; + private final Map config; // unmodifiable map + private final BufferPool bufferPool; + + JsonGeneratorFactoryImpl(Map config, boolean prettyPrinting, + BufferPool bufferPool) { + this.config = config; + this.prettyPrinting = prettyPrinting; + this.bufferPool = bufferPool; + } + + @Override + public JsonGenerator createGenerator(Writer writer) { + return prettyPrinting + ? new JsonPrettyGeneratorImpl(writer, bufferPool) + : new JsonGeneratorImpl(writer, bufferPool); + } + + @Override + public JsonGenerator createGenerator(OutputStream out) { + return prettyPrinting + ? new JsonPrettyGeneratorImpl(out, bufferPool) + : new JsonGeneratorImpl(out, bufferPool); + } + + @Override + public JsonGenerator createGenerator(OutputStream out, Charset charset) { + return prettyPrinting + ? new JsonPrettyGeneratorImpl(out, charset, bufferPool) + : new JsonGeneratorImpl(out, charset, bufferPool); + } + + @Override + public Map getConfigInUse() { + return config; + } + +} diff --git a/impl/src/main/java/org/glassfish/json/JsonGeneratorImpl.java b/impl/src/main/java/org/glassfish/json/JsonGeneratorImpl.java new file mode 100644 index 00000000..8fb49720 --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonGeneratorImpl.java @@ -0,0 +1,707 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import org.glassfish.json.api.BufferPool; + +import javax.json.*; +import javax.json.stream.JsonGenerationException; +import javax.json.stream.JsonGenerator; +import java.io.*; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Map; + +/** + * @author Jitendra Kotamraju + */ +class JsonGeneratorImpl implements JsonGenerator { + + private static final char[] INT_MIN_VALUE_CHARS = "-2147483648".toCharArray(); + private static final int[] INT_CHARS_SIZE_TABLE = { 9, 99, 999, 9999, 99999, + 999999, 9999999, 99999999, 999999999, Integer.MAX_VALUE }; + + private static final char [] DIGIT_TENS = { + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', + '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', + '3', '3', '3', '3', '3', '3', '3', '3', '3', '3', + '4', '4', '4', '4', '4', '4', '4', '4', '4', '4', + '5', '5', '5', '5', '5', '5', '5', '5', '5', '5', + '6', '6', '6', '6', '6', '6', '6', '6', '6', '6', + '7', '7', '7', '7', '7', '7', '7', '7', '7', '7', + '8', '8', '8', '8', '8', '8', '8', '8', '8', '8', + '9', '9', '9', '9', '9', '9', '9', '9', '9', '9', + } ; + + private static final char [] DIGIT_ONES = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + } ; + + /** + * All possible chars for representing a number as a String + */ + private static final char[] DIGITS = { + '0' , '1' , '2' , '3' , '4' , '5' , + '6' , '7' , '8' , '9' + }; + + private static enum Scope { + IN_NONE, + IN_OBJECT, + IN_FIELD, + IN_ARRAY + } + + private final BufferPool bufferPool; + private final Writer writer; + private Context currentContext = new Context(Scope.IN_NONE); + private final Deque stack = new ArrayDeque<>(); + + // Using own buffering mechanism as JDK's BufferedWriter uses synchronized + // methods. Also, flushBuffer() is useful when you don't want to actually + // flush the underlying output source + private final char buf[]; // capacity >= INT_MIN_VALUE_CHARS.length + private int len = 0; + + JsonGeneratorImpl(Writer writer, BufferPool bufferPool) { + this.writer = writer; + this.bufferPool = bufferPool; + this.buf = bufferPool.take(); + } + + JsonGeneratorImpl(OutputStream out, BufferPool bufferPool) { + this(out, StandardCharsets.UTF_8, bufferPool); + } + + JsonGeneratorImpl(OutputStream out, Charset encoding, BufferPool bufferPool) { + this(new OutputStreamWriter(out, encoding), bufferPool); + } + + @Override + public void flush() { + flushBuffer(); + try { + writer.flush(); + } catch (IOException ioe) { + throw new JsonException(JsonMessages.GENERATOR_FLUSH_IO_ERR(), ioe); + } + } + + @Override + public JsonGenerator writeStartObject() { + if (currentContext.scope == Scope.IN_OBJECT) { + throw new JsonGenerationException(JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope)); + } + if (currentContext.scope == Scope.IN_NONE && !currentContext.first) { + throw new JsonGenerationException(JsonMessages.GENERATOR_ILLEGAL_MULTIPLE_TEXT()); + } + writeComma(); + writeChar('{'); + stack.push(currentContext); + currentContext = new Context(Scope.IN_OBJECT); + return this; + } + + @Override + public JsonGenerator writeStartObject(String name) { + if (currentContext.scope != Scope.IN_OBJECT) { + throw new JsonGenerationException( + JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope)); + } + writeName(name); + writeChar('{'); + stack.push(currentContext); + currentContext = new Context(Scope.IN_OBJECT); + return this; + } + + private JsonGenerator writeName(String name) { + writeComma(); + writeEscapedString(name); + writeColon(); + return this; + } + + @Override + public JsonGenerator write(String name, String fieldValue) { + if (currentContext.scope != Scope.IN_OBJECT) { + throw new JsonGenerationException( + JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope)); + } + writeName(name); + writeEscapedString(fieldValue); + return this; + } + + @Override + public JsonGenerator write(String name, int value) { + if (currentContext.scope != Scope.IN_OBJECT) { + throw new JsonGenerationException( + JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope)); + } + writeName(name); + writeInt(value); + return this; + } + + @Override + public JsonGenerator write(String name, long value) { + if (currentContext.scope != Scope.IN_OBJECT) { + throw new JsonGenerationException( + JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope)); + } + writeName(name); + writeString(String.valueOf(value)); + return this; + } + + @Override + public JsonGenerator write(String name, double value) { + if (currentContext.scope != Scope.IN_OBJECT) { + throw new JsonGenerationException( + JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope)); + } + if (Double.isInfinite(value) || Double.isNaN(value)) { + throw new NumberFormatException(JsonMessages.GENERATOR_DOUBLE_INFINITE_NAN()); + } + writeName(name); + writeString(String.valueOf(value)); + return this; + } + + @Override + public JsonGenerator write(String name, BigInteger value) { + if (currentContext.scope != Scope.IN_OBJECT) { + throw new JsonGenerationException( + JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope)); + } + writeName(name); + writeString(String.valueOf(value)); + return this; + } + + @Override + public JsonGenerator write(String name, BigDecimal value) { + if (currentContext.scope != Scope.IN_OBJECT) { + throw new JsonGenerationException( + JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope)); + } + writeName(name); + writeString(String.valueOf(value)); + return this; + } + + @Override + public JsonGenerator write(String name, boolean value) { + if (currentContext.scope != Scope.IN_OBJECT) { + throw new JsonGenerationException( + JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope)); + } + writeName(name); + writeString(value? "true" : "false"); + return this; + } + + @Override + public JsonGenerator writeNull(String name) { + if (currentContext.scope != Scope.IN_OBJECT) { + throw new JsonGenerationException( + JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope)); + } + writeName(name); + writeString("null"); + return this; + } + + @Override + public JsonGenerator write(JsonValue value) { + checkContextForValue(); + + switch (value.getValueType()) { + case ARRAY: + JsonArray array = (JsonArray)value; + writeStartArray(); + for(JsonValue child: array) { + write(child); + } + writeEnd(); + break; + case OBJECT: + JsonObject object = (JsonObject)value; + writeStartObject(); + for(Map.Entry member: object.entrySet()) { + write(member.getKey(), member.getValue()); + } + writeEnd(); + break; + case STRING: + JsonString str = (JsonString)value; + write(str.getString()); + break; + case NUMBER: + JsonNumber number = (JsonNumber)value; + writeValue(number.toString()); + popFieldContext(); + break; + case TRUE: + write(true); + break; + case FALSE: + write(false); + break; + case NULL: + writeNull(); + break; + } + + return this; + } + + @Override + public JsonGenerator writeStartArray() { + if (currentContext.scope == Scope.IN_OBJECT) { + throw new JsonGenerationException(JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope)); + } + if (currentContext.scope == Scope.IN_NONE && !currentContext.first) { + throw new JsonGenerationException(JsonMessages.GENERATOR_ILLEGAL_MULTIPLE_TEXT()); + } + writeComma(); + writeChar('['); + stack.push(currentContext); + currentContext = new Context(Scope.IN_ARRAY); + return this; + } + + @Override + public JsonGenerator writeStartArray(String name) { + if (currentContext.scope != Scope.IN_OBJECT) { + throw new JsonGenerationException( + JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope)); + } + writeName(name); + writeChar('['); + stack.push(currentContext); + currentContext = new Context(Scope.IN_ARRAY); + return this; + } + + @Override + public JsonGenerator write(String name, JsonValue value) { + if (currentContext.scope != Scope.IN_OBJECT) { + throw new JsonGenerationException( + JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope)); + } + switch (value.getValueType()) { + case ARRAY: + JsonArray array = (JsonArray)value; + writeStartArray(name); + for(JsonValue child: array) { + write(child); + } + writeEnd(); + break; + case OBJECT: + JsonObject object = (JsonObject)value; + writeStartObject(name); + for(Map.Entry member: object.entrySet()) { + write(member.getKey(), member.getValue()); + } + writeEnd(); + break; + case STRING: + JsonString str = (JsonString)value; + write(name, str.getString()); + break; + case NUMBER: + JsonNumber number = (JsonNumber)value; + writeValue(name, number.toString()); + break; + case TRUE: + write(name, true); + break; + case FALSE: + write(name, false); + break; + case NULL: + writeNull(name); + break; + } + return this; + } + + @Override + public JsonGenerator write(String value) { + checkContextForValue(); + writeComma(); + writeEscapedString(value); + popFieldContext(); + return this; + } + + + @Override + public JsonGenerator write(int value) { + checkContextForValue(); + writeComma(); + writeInt(value); + popFieldContext(); + return this; + } + + @Override + public JsonGenerator write(long value) { + checkContextForValue(); + writeValue(String.valueOf(value)); + popFieldContext(); + return this; + } + + @Override + public JsonGenerator write(double value) { + checkContextForValue(); + if (Double.isInfinite(value) || Double.isNaN(value)) { + throw new NumberFormatException(JsonMessages.GENERATOR_DOUBLE_INFINITE_NAN()); + } + writeValue(String.valueOf(value)); + popFieldContext(); + return this; + } + + @Override + public JsonGenerator write(BigInteger value) { + checkContextForValue(); + writeValue(value.toString()); + popFieldContext(); + return this; + } + + private void checkContextForValue() { + if ((!currentContext.first && currentContext.scope != Scope.IN_ARRAY && currentContext.scope != Scope.IN_FIELD) + || (currentContext.first && currentContext.scope == Scope.IN_OBJECT)) { + throw new JsonGenerationException( + JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope)); + } + } + + @Override + public JsonGenerator write(BigDecimal value) { + checkContextForValue(); + writeValue(value.toString()); + popFieldContext(); + + return this; + } + + private void popFieldContext() { + if (currentContext.scope == Scope.IN_FIELD) { + currentContext = stack.pop(); + } + } + + @Override + public JsonGenerator write(boolean value) { + checkContextForValue(); + writeComma(); + writeString(value ? "true" : "false"); + popFieldContext(); + return this; + } + + @Override + public JsonGenerator writeNull() { + checkContextForValue(); + writeComma(); + writeString("null"); + popFieldContext(); + return this; + } + + private void writeValue(String value) { + writeComma(); + writeString(value); + } + + private void writeValue(String name, String value) { + writeComma(); + writeEscapedString(name); + writeColon(); + writeString(value); + } + + @Override + public JsonGenerator writeKey(String name) { + if (currentContext.scope != Scope.IN_OBJECT) { + throw new JsonGenerationException( + JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope)); + } + writeName(name); + stack.push(currentContext); + currentContext = new Context(Scope.IN_FIELD); + currentContext.first = false; + return this; + } + + @Override + public JsonGenerator writeEnd() { + if (currentContext.scope == Scope.IN_NONE) { + throw new JsonGenerationException("writeEnd() cannot be called in no context"); + } + writeChar(currentContext.scope == Scope.IN_ARRAY ? ']' : '}'); + currentContext = stack.pop(); + popFieldContext(); + return this; + } + + protected void writeComma() { + if (!currentContext.first && currentContext.scope != Scope.IN_FIELD) { + writeChar(','); + } + currentContext.first = false; + } + + protected void writeColon() { + writeChar(':'); + } + + private static class Context { + boolean first = true; + final Scope scope; + + Context(Scope scope) { + this.scope = scope; + } + + } + + @Override + public void close() { + if (currentContext.scope != Scope.IN_NONE || currentContext.first) { + throw new JsonGenerationException(JsonMessages.GENERATOR_INCOMPLETE_JSON()); + } + flushBuffer(); + try { + writer.close(); + } catch (IOException ioe) { + throw new JsonException(JsonMessages.GENERATOR_CLOSE_IO_ERR(), ioe); + } + bufferPool.recycle(buf); + } + + // begin, end-1 indexes represent characters that need not + // be escaped + // + // XXXssssssssssssXXXXXXXXXXXXXXXXXXXXXXrrrrrrrrrrrrrrXXXXXX + // ^ ^ ^ ^ + // | | | | + // begin end begin end + void writeEscapedString(String string) { + writeChar('"'); + int len = string.length(); + for(int i = 0; i < len; i++) { + int begin = i, end = i; + char c = string.charAt(i); + // find all the characters that need not be escaped + // unescaped = %x20-21 | %x23-5B | %x5D-10FFFF + while(c >= 0x20 && c <= 0x10ffff && c != 0x22 && c != 0x5c) { + i++; end = i; + if (i < len) { + c = string.charAt(i); + } else { + break; + } + } + // Write characters without escaping + if (begin < end) { + writeString(string, begin, end); + if (i == len) { + break; + } + } + + switch (c) { + case '"': + case '\\': + writeChar('\\'); writeChar(c); + break; + case '\b': + writeChar('\\'); writeChar('b'); + break; + case '\f': + writeChar('\\'); writeChar('f'); + break; + case '\n': + writeChar('\\'); writeChar('n'); + break; + case '\r': + writeChar('\\'); writeChar('r'); + break; + case '\t': + writeChar('\\'); writeChar('t'); + break; + default: + String hex = "000" + Integer.toHexString(c); + writeString("\\u" + hex.substring(hex.length() - 4)); + } + } + writeChar('"'); + } + + void writeString(String str, int begin, int end) { + while (begin < end) { // source begin and end indexes + int no = Math.min(buf.length - len, end - begin); + str.getChars(begin, begin + no, buf, len); + begin += no; // Increment source index + len += no; // Increment dest index + if (len >= buf.length) { + flushBuffer(); + } + } + } + + void writeString(String str) { + writeString(str, 0, str.length()); + } + + void writeChar(char c) { + if (len >= buf.length) { + flushBuffer(); + } + buf[len++] = c; + } + + // Not using Integer.toString() since it creates intermediary String + // Also, we want the chars to be copied to our buffer directly + void writeInt(int num) { + int size; + if (num == Integer.MIN_VALUE) { + size = INT_MIN_VALUE_CHARS.length; + } else { + size = (num < 0) ? stringSize(-num) + 1 : stringSize(num); + } + if (len+size >= buf.length) { + flushBuffer(); + } + if (num == Integer.MIN_VALUE) { + System.arraycopy(INT_MIN_VALUE_CHARS, 0, buf, len, size); + } else { + fillIntChars(num, buf, len+size); + } + len += size; + } + + // flushBuffer writes the buffered contents to writer. But incase of + // byte stream, an OuputStreamWriter is created and that buffers too. + // We may need to call OutputStreamWriter#flushBuffer() using + // reflection if that is really required (commented out below) + void flushBuffer() { + try { + if (len > 0) { + writer.write(buf, 0, len); + len = 0; + } + } catch (IOException ioe) { + throw new JsonException(JsonMessages.GENERATOR_WRITE_IO_ERR(), ioe); + } + } + +// private static final Method flushBufferMethod; +// static { +// Method m = null; +// try { +// m = OutputStreamWriter.class.getDeclaredMethod("flushBuffer"); +// m.setAccessible(true); +// } catch (Exception e) { +// // no-op +// } +// flushBufferMethod = m; +// } +// void flushBufferOSW() { +// flushBuffer(); +// if (writer instanceof OutputStreamWriter) { +// try { +// flushBufferMethod.invoke(writer); +// } catch (Exception e) { +// // no-op +// } +// } +// } + + // Requires positive x + private static int stringSize(int x) { + for (int i=0; ; i++) + if (x <= INT_CHARS_SIZE_TABLE[i]) + return i+1; + } + + /** + * Places characters representing the integer i into the + * character array buf. The characters are placed into + * the buffer backwards starting with the least significant + * digit at the specified index (exclusive), and working + * backwards from there. + * + * Will fail if i == Integer.MIN_VALUE + */ + private static void fillIntChars(int i, char[] buf, int index) { + int q, r; + int charPos = index; + char sign = 0; + + if (i < 0) { + sign = '-'; + i = -i; + } + + // Generate two digits per iteration + while (i >= 65536) { + q = i / 100; + // really: r = i - (q * 100); + r = i - ((q << 6) + (q << 5) + (q << 2)); + i = q; + buf [--charPos] = DIGIT_ONES[r]; + buf [--charPos] = DIGIT_TENS[r]; + } + + // Fall thru to fast mode for smaller numbers + // assert(i <= 65536, i); + for (;;) { + q = (i * 52429) >>> (16+3); + r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ... + buf [--charPos] = DIGITS[r]; + i = q; + if (i == 0) break; + } + if (sign != 0) { + buf [--charPos] = sign; + } + } + +} diff --git a/impl/src/main/java/org/glassfish/json/JsonLocationImpl.java b/impl/src/main/java/org/glassfish/json/JsonLocationImpl.java new file mode 100644 index 00000000..048c2860 --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonLocationImpl.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import javax.json.stream.JsonLocation; + +/** + * @author Jitendra Kotamraju + */ +class JsonLocationImpl implements JsonLocation { + static final JsonLocation UNKNOWN = new JsonLocationImpl(-1, -1, -1); + + private final long columnNo; + private final long lineNo; + private final long offset; + + JsonLocationImpl(long lineNo, long columnNo, long streamOffset) { + this.lineNo = lineNo; + this.columnNo = columnNo; + this.offset = streamOffset; + } + + @Override + public long getLineNumber() { + return lineNo; + } + + @Override + public long getColumnNumber() { + return columnNo; + } + + @Override + public long getStreamOffset() { + return offset; + } + + @Override + public String toString() { + return "(line no="+lineNo+", column no="+columnNo+", offset="+ offset +")"; + } + +} diff --git a/impl/src/main/java/org/glassfish/json/JsonMergePatchImpl.java b/impl/src/main/java/org/glassfish/json/JsonMergePatchImpl.java new file mode 100644 index 00000000..bd9fdd99 --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonMergePatchImpl.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import javax.json.Json; +import javax.json.JsonMergePatch; +import javax.json.JsonObject; +import javax.json.JsonObjectBuilder; +import javax.json.JsonValue; + +/** + * This class is an implementation of a JSON Merge Patch as specified in + * RFC 7396. + * + * @since 1.1 + */ + +public final class JsonMergePatchImpl implements JsonMergePatch { + + private JsonValue patch; + + public JsonMergePatchImpl(JsonValue patch) { + this.patch = patch; + } + + @Override + public JsonValue apply(JsonValue target) { + return mergePatch(target, patch); + } + + @Override + public JsonValue toJsonValue() { + return patch; + } + /** + * Applies the specified patch to the specified target. + * The target is not modified by the patch. + * + * @param target the {@code JsonValue} to apply the patch operations + * @param patch the patch + * @return the {@code JsonValue} as the result of applying the patch + * operations on the target. + */ + private static JsonValue mergePatch(JsonValue target, JsonValue patch) { + + if (patch.getValueType() != JsonValue.ValueType.OBJECT) { + return patch; + } + if (target.getValueType() != JsonValue.ValueType.OBJECT) { + target = JsonValue.EMPTY_JSON_OBJECT; + } + JsonObject targetJsonObject = target.asJsonObject(); + JsonObjectBuilder builder = + Json.createObjectBuilder(targetJsonObject); + patch.asJsonObject().forEach((key, value) -> { + if (value == JsonValue.NULL) { + if (targetJsonObject.containsKey(key)) { + builder.remove(key); + } + } else if (targetJsonObject.containsKey(key)) { + builder.add(key, mergePatch(targetJsonObject.get(key), value)); + } else { + builder.add(key, mergePatch(JsonValue.EMPTY_JSON_OBJECT, value)); + } + }); + return builder.build(); + } + + /** + * Generate a JSON Merge Patch from the source and target {@code JsonValue}. + * @param source the source + * @param target the target + * @return a JSON Patch which when applied to the source, yields the target + */ + static JsonValue diff(JsonValue source, JsonValue target) { + if (source.getValueType() != JsonValue.ValueType.OBJECT || + target.getValueType() != JsonValue.ValueType.OBJECT) { + return target; + } + JsonObject s = (JsonObject) source; + JsonObject t = (JsonObject) target; + JsonObjectBuilder builder = Json.createObjectBuilder(); + // First find members to be replaced or removed + s.forEach((key, value) -> { + if (t.containsKey(key)) { + // key present in both. + if (! value.equals(t.get(key))) { + // If the values are equal, nop, else get diff for the values + builder.add(key, diff(value, t.get(key))); + } + } else { + builder.addNull(key); + } + }); + // Then find members to be added + t.forEach((key, value) -> { + if (! s.containsKey(key)) + builder.add(key, value); + }); + return builder.build(); + } + +} + diff --git a/impl/src/main/java/org/glassfish/json/JsonMessages.java b/impl/src/main/java/org/glassfish/json/JsonMessages.java new file mode 100644 index 00000000..b2afca91 --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonMessages.java @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + + +import javax.json.stream.JsonLocation; +import javax.json.stream.JsonParser; +import java.text.MessageFormat; +import java.util.ResourceBundle; +import javax.json.JsonObject; +import javax.json.JsonValue; + +/** + * Defines string formatting method for each constant in the resource file + * + * @author Jitendra Kotamraju + */ +final class JsonMessages { + private static final ResourceBundle BUNDLE = + ResourceBundle.getBundle("org.glassfish.json.messages"); + + // global/shared messages + static String INTERNAL_ERROR() { + return localize("internal.error"); + } + + // tokenizer messages + static String TOKENIZER_UNEXPECTED_CHAR(int unexpected, JsonLocation location) { + return localize("tokenizer.unexpected.char", unexpected, location); + } + + static String TOKENIZER_EXPECTED_CHAR(int unexpected, JsonLocation location, char expected) { + return localize("tokenizer.expected.char", unexpected, location, expected); + } + + static String TOKENIZER_IO_ERR() { + return localize("tokenizer.io.err"); + } + + + // parser messages + static String PARSER_GETSTRING_ERR(JsonParser.Event event) { + return localize("parser.getString.err", event); + } + + static String PARSER_ISINTEGRALNUMBER_ERR(JsonParser.Event event) { + return localize("parser.isIntegralNumber.err", event); + } + + static String PARSER_GETINT_ERR(JsonParser.Event event) { + return localize("parser.getInt.err", event); + } + + static String PARSER_GETLONG_ERR(JsonParser.Event event) { + return localize("parser.getLong.err", event); + } + + static String PARSER_GETBIGDECIMAL_ERR(JsonParser.Event event) { + return localize("parser.getBigDecimal.err", event); + } + + static String PARSER_GETARRAY_ERR(JsonParser.Event event) { + return localize("parser.getArray.err", event); + } + + static String PARSER_GETOBJECT_ERR(JsonParser.Event event) { + return localize("parser.getObject.err", event); + } + + static String PARSER_GETVALUE_ERR(JsonParser.Event event) { + return localize("parser.getValue.err", event); + } + + static String PARSER_GETVALUESTREAM_ERR() { + return localize("parser.getValueStream.err"); + } + + static String PARSER_EXPECTED_EOF(JsonTokenizer.JsonToken token) { + return localize("parser.expected.eof", token); + } + + static String PARSER_TOKENIZER_CLOSE_IO() { + return localize("parser.tokenizer.close.io"); + } + + static String PARSER_INVALID_TOKEN(JsonTokenizer.JsonToken token, JsonLocation location, String expectedTokens) { + return localize("parser.invalid.token", token, location, expectedTokens); + } + + static String PARSER_STATE_ERR(JsonValue.ValueType type) { + return localize("parser.state.err", type); + } + + static String PARSER_SCOPE_ERR(JsonValue value) { + return localize("parser.scope.err", value); + } + + static String PARSER_INPUT_ENC_DETECT_FAILED() { + return localize("parser.input.enc.detect.failed"); + } + + static String PARSER_INPUT_ENC_DETECT_IOERR() { + return localize("parser.input.enc.detect.ioerr"); + } + + // generator messages + static String GENERATOR_FLUSH_IO_ERR() { + return localize("generator.flush.io.err"); + } + + static String GENERATOR_CLOSE_IO_ERR() { + return localize("generator.close.io.err"); + } + + static String GENERATOR_WRITE_IO_ERR() { + return localize("generator.write.io.err"); + } + + static String GENERATOR_ILLEGAL_METHOD(Object scope) { + return localize("generator.illegal.method", scope); + } + + static String GENERATOR_DOUBLE_INFINITE_NAN() { + return localize("generator.double.infinite.nan"); + } + + static String GENERATOR_INCOMPLETE_JSON() { + return localize("generator.incomplete.json"); + } + + static String GENERATOR_ILLEGAL_MULTIPLE_TEXT() { + return localize("generator.illegal.multiple.text"); + } + + + + // writer messages + static String WRITER_WRITE_ALREADY_CALLED() { + return localize("writer.write.already.called"); + } + + // reader messages + static String READER_READ_ALREADY_CALLED() { + return localize("reader.read.already.called"); + } + + + // obj builder messages + static String OBJBUILDER_NAME_NULL() { + return localize("objbuilder.name.null"); + } + + static String OBJBUILDER_VALUE_NULL() { + return localize("objbuilder.value.null"); + } + + static String OBJBUILDER_OBJECT_BUILDER_NULL() { + return localize("objbuilder.object.builder.null"); + } + + static String OBJBUILDER_ARRAY_BUILDER_NULL() { + return localize("objbuilder.array.builder.null"); + } + + + // array builder messages + static String ARRBUILDER_VALUE_NULL() { + return localize("arrbuilder.value.null"); + } + + static String ARRBUILDER_OBJECT_BUILDER_NULL() { + return localize("arrbuilder.object.builder.null"); + } + + static String ARRBUILDER_ARRAY_BUILDER_NULL() { + return localize("arrbuilder.array.builder.null"); + } + + static String ARRBUILDER_VALUELIST_NULL(int index, int size) { + return localize("arrbuilder.valuelist.null", index, size); + } + + // json pointer messages + static String POINTER_FORMAT_INVALID() { + return localize("pointer.format.invalid"); + } + + static String POINTER_MAPPING_MISSING(JsonObject object, String key) { + return localize("pointer.mapping.missing", object, key); + } + + static String POINTER_REFERENCE_INVALID(JsonValue.ValueType type) { + return localize("pointer.reference.invalid", type.name()); + } + + static String POINTER_ARRAY_INDEX_ERR(String token) { + return localize("pointer.array.index.err", token); + } + + static String POINTER_ARRAY_INDEX_ILLEGAL(String token) { + return localize("pointer.array.index.illegal", token); + } + + // nodereference messages + static String NODEREF_VALUE_ADD_ERR() { + return localize("noderef.value.add.err"); + } + + static String NODEREF_VALUE_CANNOT_REMOVE() { + return localize("noderef.value.cannot.remove"); + } + + static String NODEREF_OBJECT_MISSING(String key) { + return localize("noderef.object.missing", key); + } + + static String NODEREF_ARRAY_INDEX_ERR(int index, int size) { + return localize("noderef.array.index.err", index, size); + } + + // json patch messages + static String PATCH_MUST_BE_ARRAY() { + return localize("patch.must.be.array"); + } + + static String PATCH_MOVE_PROPER_PREFIX(String from, String path) { + return localize("patch.move.proper.prefix", from, path); + } + + static String PATCH_MOVE_TARGET_NULL(String from) { + return localize("patch.move.target.null", from); + } + + static String PATCH_TEST_FAILED(String path, String value) { + return localize("patch.test.failed", path, value); + } + + static String PATCH_ILLEGAL_OPERATION(String operation) { + return localize("patch.illegal.operation", operation); + } + + static String PATCH_MEMBER_MISSING(String operation, String member) { + return localize("patch.member.missing", operation, member); + } + + + private static String localize(String key, Object ... args) { + try { + String msg = BUNDLE.getString(key); + return MessageFormat.format(msg, args); + } catch (Exception e) { + return getDefaultMessage(key, args); + } + } + + private static String getDefaultMessage(String key, Object ... args) { + StringBuilder sb = new StringBuilder(); + sb.append("[failed to localize] "); + sb.append(key); + if (args != null) { + sb.append('('); + for (int i = 0; i < args.length; ++i) { + if (i != 0) + sb.append(", "); + sb.append(String.valueOf(args[i])); + } + sb.append(')'); + } + return sb.toString(); + } + +} diff --git a/impl/src/main/java/org/glassfish/json/JsonNumberImpl.java b/impl/src/main/java/org/glassfish/json/JsonNumberImpl.java new file mode 100644 index 00000000..5be3828b --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonNumberImpl.java @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import javax.json.JsonNumber; +import java.math.BigDecimal; +import java.math.BigInteger; + +/** + * JsonNumber impl. Subclasses provide optimized implementations + * when backed by int, long, BigDecimal + * + * @author Jitendra Kotamraju + */ +abstract class JsonNumberImpl implements JsonNumber { + + static JsonNumber getJsonNumber(int num) { + return new JsonIntNumber(num); + } + + static JsonNumber getJsonNumber(long num) { + return new JsonLongNumber(num); + } + + static JsonNumber getJsonNumber(BigInteger value) { + return new JsonBigDecimalNumber(new BigDecimal(value)); + } + + static JsonNumber getJsonNumber(double value) { + //bigDecimal = new BigDecimal(value); + // This is the preferred way to convert double to BigDecimal + return new JsonBigDecimalNumber(BigDecimal.valueOf(value)); + } + + static JsonNumber getJsonNumber(BigDecimal value) { + return new JsonBigDecimalNumber(value); + } + + // Optimized JsonNumber impl for int numbers. + private static final class JsonIntNumber extends JsonNumberImpl { + private final int num; + private BigDecimal bigDecimal; // assigning it lazily on demand + + JsonIntNumber(int num) { + this.num = num; + } + + @Override + public boolean isIntegral() { + return true; + } + + @Override + public int intValue() { + return num; + } + + @Override + public int intValueExact() { + return num; + } + + @Override + public long longValue() { + return num; + } + + @Override + public long longValueExact() { + return num; + } + + @Override + public double doubleValue() { + return num; + } + + @Override + public BigDecimal bigDecimalValue() { + // reference assignments are atomic. At the most some more temp + // BigDecimal objects are created + BigDecimal bd = bigDecimal; + if (bd == null) { + bigDecimal = bd = new BigDecimal(num); + } + return bd; + } + + @Override + public Number numberValue() { + return num; + } + + @Override + public String toString() { + return Integer.toString(num); + } + } + + // Optimized JsonNumber impl for long numbers. + private static final class JsonLongNumber extends JsonNumberImpl { + private final long num; + private BigDecimal bigDecimal; // assigning it lazily on demand + + JsonLongNumber(long num) { + this.num = num; + } + + @Override + public boolean isIntegral() { + return true; + } + + @Override + public int intValue() { + return (int) num; + } + + @Override + public int intValueExact() { + return Math.toIntExact(num); + } + + @Override + public long longValue() { + return num; + } + + @Override + public long longValueExact() { + return num; + } + + @Override + public double doubleValue() { + return num; + } + + @Override + public BigDecimal bigDecimalValue() { + // reference assignments are atomic. At the most some more temp + // BigDecimal objects are created + BigDecimal bd = bigDecimal; + if (bd == null) { + bigDecimal = bd = new BigDecimal(num); + } + return bd; + } + + @Override + public Number numberValue() { + return num; + } + + @Override + public String toString() { + return Long.toString(num); + } + + } + + // JsonNumber impl using BigDecimal numbers. + private static final class JsonBigDecimalNumber extends JsonNumberImpl { + private final BigDecimal bigDecimal; + + JsonBigDecimalNumber(BigDecimal value) { + this.bigDecimal = value; + } + + @Override + public BigDecimal bigDecimalValue() { + return bigDecimal; + } + + @Override + public Number numberValue() { + return bigDecimalValue(); + } + + } + + @Override + public boolean isIntegral() { + return bigDecimalValue().scale() == 0; + } + + @Override + public int intValue() { + return bigDecimalValue().intValue(); + } + + @Override + public int intValueExact() { + return bigDecimalValue().intValueExact(); + } + + @Override + public long longValue() { + return bigDecimalValue().longValue(); + } + + @Override + public long longValueExact() { + return bigDecimalValue().longValueExact(); + } + + @Override + public double doubleValue() { + return bigDecimalValue().doubleValue(); + } + + @Override + public BigInteger bigIntegerValue() { + return bigDecimalValue().toBigInteger(); + } + + @Override + public BigInteger bigIntegerValueExact() { + return bigDecimalValue().toBigIntegerExact(); + } + + @Override + public ValueType getValueType() { + return ValueType.NUMBER; + } + + @Override + public int hashCode() { + return bigDecimalValue().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj){ + return true; + } + if (!(obj instanceof JsonNumber)) { + return false; + } + JsonNumber other = (JsonNumber)obj; + return bigDecimalValue().equals(other.bigDecimalValue()); + } + + @Override + public String toString() { + return bigDecimalValue().toString(); + } + +} + diff --git a/impl/src/main/java/org/glassfish/json/JsonObjectBuilderImpl.java b/impl/src/main/java/org/glassfish/json/JsonObjectBuilderImpl.java new file mode 100644 index 00000000..9e7e5a14 --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonObjectBuilderImpl.java @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import org.glassfish.json.api.BufferPool; + +import javax.json.JsonArrayBuilder; +import javax.json.*; +import java.io.StringWriter; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.*; + +/** + * JsonObjectBuilder implementation + * + * @author Jitendra Kotamraju + * @author Kin-man Chung + */ +class JsonObjectBuilderImpl implements JsonObjectBuilder { + + private Map valueMap; + private final BufferPool bufferPool; + + JsonObjectBuilderImpl(BufferPool bufferPool) { + this.bufferPool = bufferPool; + } + + JsonObjectBuilderImpl(JsonObject object, BufferPool bufferPool) { + this.bufferPool = bufferPool; + valueMap = new LinkedHashMap<>(); + valueMap.putAll(object); + } + + JsonObjectBuilderImpl(Map map, BufferPool bufferPool) { + this.bufferPool = bufferPool; + valueMap = new LinkedHashMap<>(); + populate(map); + } + + @Override + public JsonObjectBuilder add(String name, JsonValue value) { + validateName(name); + validateValue(value); + putValueMap(name, value); + return this; + } + + @Override + public JsonObjectBuilder add(String name, String value) { + validateName(name); + validateValue(value); + putValueMap(name, new JsonStringImpl(value)); + return this; + } + + @Override + public JsonObjectBuilder add(String name, BigInteger value) { + validateName(name); + validateValue(value); + putValueMap(name, JsonNumberImpl.getJsonNumber(value)); + return this; + } + + @Override + public JsonObjectBuilder add(String name, BigDecimal value) { + validateName(name); + validateValue(value); + putValueMap(name, JsonNumberImpl.getJsonNumber(value)); + return this; + } + + @Override + public JsonObjectBuilder add(String name, int value) { + validateName(name); + putValueMap(name, JsonNumberImpl.getJsonNumber(value)); + return this; + } + + @Override + public JsonObjectBuilder add(String name, long value) { + validateName(name); + putValueMap(name, JsonNumberImpl.getJsonNumber(value)); + return this; + } + + @Override + public JsonObjectBuilder add(String name, double value) { + validateName(name); + putValueMap(name, JsonNumberImpl.getJsonNumber(value)); + return this; + } + + @Override + public JsonObjectBuilder add(String name, boolean value) { + validateName(name); + putValueMap(name, value ? JsonValue.TRUE : JsonValue.FALSE); + return this; + } + + @Override + public JsonObjectBuilder addNull(String name) { + validateName(name); + putValueMap(name, JsonValue.NULL); + return this; + } + + @Override + public JsonObjectBuilder add(String name, JsonObjectBuilder builder) { + validateName(name); + if (builder == null) { + throw new NullPointerException(JsonMessages.OBJBUILDER_OBJECT_BUILDER_NULL()); + } + putValueMap(name, builder.build()); + return this; + } + + @Override + public JsonObjectBuilder add(String name, JsonArrayBuilder builder) { + validateName(name); + if (builder == null) { + throw new NullPointerException(JsonMessages.OBJBUILDER_ARRAY_BUILDER_NULL()); + } + putValueMap(name, builder.build()); + return this; + } + + @Override + public JsonObjectBuilder addAll(JsonObjectBuilder builder) { + if (builder == null) { + throw new NullPointerException(JsonMessages.OBJBUILDER_OBJECT_BUILDER_NULL()); + } + if (valueMap == null) { + this.valueMap = new LinkedHashMap<>(); + } + this.valueMap.putAll(builder.build()); + return this; + } + + @Override + public JsonObjectBuilder remove(String name) { + validateName(name); + this.valueMap.remove(name); + return this; + } + + @Override + public JsonObject build() { + Map snapshot = (valueMap == null) + ? Collections.emptyMap() + : Collections.unmodifiableMap(valueMap); + valueMap = null; + return new JsonObjectImpl(snapshot, bufferPool); + } + + private void populate(Map map) { + final Set fields = map.keySet(); + for (String field : fields) { + Object value = map.get(field); + if (value != null && value instanceof Optional) { + ((Optional) value).ifPresent(v -> + this.valueMap.put(field, MapUtil.handle(v, bufferPool))); + } else { + this.valueMap.put(field, MapUtil.handle(value, bufferPool)); + } + } + } + + private void putValueMap(String name, JsonValue value) { + if (valueMap == null) { + this.valueMap = new LinkedHashMap<>(); + } + valueMap.put(name, value); + } + + private void validateName(String name) { + if (name == null) { + throw new NullPointerException(JsonMessages.OBJBUILDER_NAME_NULL()); + } + } + + private void validateValue(Object value) { + if (value == null) { + throw new NullPointerException(JsonMessages.OBJBUILDER_VALUE_NULL()); + } + } + + private static final class JsonObjectImpl extends AbstractMap implements JsonObject { + private final Map valueMap; // unmodifiable + private final BufferPool bufferPool; + + JsonObjectImpl(Map valueMap, BufferPool bufferPool) { + this.valueMap = valueMap; + this.bufferPool = bufferPool; + } + + @Override + public JsonArray getJsonArray(String name) { + return (JsonArray)get(name); + } + + @Override + public JsonObject getJsonObject(String name) { + return (JsonObject)get(name); + } + + @Override + public JsonNumber getJsonNumber(String name) { + return (JsonNumber)get(name); + } + + @Override + public JsonString getJsonString(String name) { + return (JsonString)get(name); + } + + @Override + public String getString(String name) { + return getJsonString(name).getString(); + } + + @Override + public String getString(String name, String defaultValue) { + try { + return getString(name); + } catch (Exception e) { + return defaultValue; + } + } + + @Override + public int getInt(String name) { + return getJsonNumber(name).intValue(); + } + + @Override + public int getInt(String name, int defaultValue) { + try { + return getInt(name); + } catch (Exception e) { + return defaultValue; + } + } + + @Override + public boolean getBoolean(String name) { + JsonValue value = get(name); + if (value == null) { + throw new NullPointerException(); + } else if (value == JsonValue.TRUE) { + return true; + } else if (value == JsonValue.FALSE) { + return false; + } else { + throw new ClassCastException(); + } + } + + @Override + public boolean getBoolean(String name, boolean defaultValue) { + try { + return getBoolean(name); + } catch (Exception e) { + return defaultValue; + } + } + + @Override + public boolean isNull(String name) { + return get(name).equals(JsonValue.NULL); + } + + @Override + public ValueType getValueType() { + return ValueType.OBJECT; + } + + @Override + public Set> entrySet() { + return valueMap.entrySet(); + } + + @Override + public String toString() { + StringWriter sw = new StringWriter(); + try (JsonWriter jw = new JsonWriterImpl(sw, bufferPool)) { + jw.write(this); + } + return sw.toString(); + } + + @Override + public JsonObject asJsonObject() { + return this; + } + + @Override + public int size() { + return valueMap.size(); + } + + @Override + public JsonValue get(Object key) { + return valueMap.get(key); + } + + @Override + public boolean containsKey(Object key) { + return valueMap.containsKey(key); + } + } + +} diff --git a/impl/src/main/java/org/glassfish/json/JsonParserFactoryImpl.java b/impl/src/main/java/org/glassfish/json/JsonParserFactoryImpl.java new file mode 100644 index 00000000..7502e63c --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonParserFactoryImpl.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import org.glassfish.json.api.BufferPool; + +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.json.stream.JsonParserFactory; +import javax.json.stream.JsonParser; +import java.io.InputStream; +import java.io.Reader; +import java.nio.charset.Charset; +import java.util.Collections; +import java.util.Map; + +/** + * @author Jitendra Kotamraju + */ +class JsonParserFactoryImpl implements JsonParserFactory { + private final Map config = Collections.emptyMap(); + private final BufferPool bufferPool; + + JsonParserFactoryImpl(BufferPool bufferPool) { + this.bufferPool = bufferPool; + } + + @Override + public JsonParser createParser(Reader reader) { + return new JsonParserImpl(reader, bufferPool); + } + + @Override + public JsonParser createParser(InputStream in) { + return new JsonParserImpl(in, bufferPool); + } + + @Override + public JsonParser createParser(InputStream in, Charset charset) { + return new JsonParserImpl(in, charset, bufferPool); + } + + @Override + public JsonParser createParser(JsonArray array) { + return new JsonStructureParser(array); + } + + @Override + public Map getConfigInUse() { + return config; + } + + @Override + public JsonParser createParser(JsonObject object) { + return new JsonStructureParser(object); + } +} diff --git a/impl/src/main/java/org/glassfish/json/JsonParserImpl.java b/impl/src/main/java/org/glassfish/json/JsonParserImpl.java new file mode 100644 index 00000000..1857a92b --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonParserImpl.java @@ -0,0 +1,539 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.math.BigDecimal; +import java.nio.charset.Charset; +import java.util.AbstractMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.Consumer; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import javax.json.JsonArray; +import javax.json.JsonArrayBuilder; +import javax.json.JsonException; +import javax.json.JsonObject; +import javax.json.JsonObjectBuilder; +import javax.json.JsonValue; +import javax.json.stream.JsonLocation; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; +import javax.json.stream.JsonParsingException; + +import org.glassfish.json.JsonTokenizer.JsonToken; +import org.glassfish.json.api.BufferPool; + +/** + * JSON parser implementation. NoneContext, ArrayContext, ObjectContext is used + * to go to next parser state. + * + * @author Jitendra Kotamraju + * @author Kin-man Chung + */ +public class JsonParserImpl implements JsonParser { + + private final BufferPool bufferPool; + private Context currentContext = new NoneContext(); + private Event currentEvent; + + private final Stack stack = new Stack(); + private final JsonTokenizer tokenizer; + + public JsonParserImpl(Reader reader, BufferPool bufferPool) { + this.bufferPool = bufferPool; + tokenizer = new JsonTokenizer(reader, bufferPool); + } + + public JsonParserImpl(InputStream in, BufferPool bufferPool) { + this.bufferPool = bufferPool; + UnicodeDetectingInputStream uin = new UnicodeDetectingInputStream(in); + tokenizer = new JsonTokenizer(new InputStreamReader(uin, uin.getCharset()), bufferPool); + } + + public JsonParserImpl(InputStream in, Charset encoding, BufferPool bufferPool) { + this.bufferPool = bufferPool; + tokenizer = new JsonTokenizer(new InputStreamReader(in, encoding), bufferPool); + } + + @Override + public String getString() { + if (currentEvent == Event.KEY_NAME || currentEvent == Event.VALUE_STRING + || currentEvent == Event.VALUE_NUMBER) { + return tokenizer.getValue(); + } + throw new IllegalStateException( + JsonMessages.PARSER_GETSTRING_ERR(currentEvent)); + } + + @Override + public boolean isIntegralNumber() { + if (currentEvent != Event.VALUE_NUMBER) { + throw new IllegalStateException( + JsonMessages.PARSER_ISINTEGRALNUMBER_ERR(currentEvent)); + } + return tokenizer.isIntegral(); + } + + @Override + public int getInt() { + if (currentEvent != Event.VALUE_NUMBER) { + throw new IllegalStateException( + JsonMessages.PARSER_GETINT_ERR(currentEvent)); + } + return tokenizer.getInt(); + } + + boolean isDefinitelyInt() { + return tokenizer.isDefinitelyInt(); + } + + boolean isDefinitelyLong() { + return tokenizer.isDefinitelyLong(); + } + + @Override + public long getLong() { + if (currentEvent != Event.VALUE_NUMBER) { + throw new IllegalStateException( + JsonMessages.PARSER_GETLONG_ERR(currentEvent)); + } + return tokenizer.getLong(); + } + + @Override + public BigDecimal getBigDecimal() { + if (currentEvent != Event.VALUE_NUMBER) { + throw new IllegalStateException( + JsonMessages.PARSER_GETBIGDECIMAL_ERR(currentEvent)); + } + return tokenizer.getBigDecimal(); + } + + @Override + public JsonArray getArray() { + if (currentEvent != Event.START_ARRAY) { + throw new IllegalStateException( + JsonMessages.PARSER_GETARRAY_ERR(currentEvent)); + } + return getArray(new JsonArrayBuilderImpl(bufferPool)); + } + + @Override + public JsonObject getObject() { + if (currentEvent != Event.START_OBJECT) { + throw new IllegalStateException( + JsonMessages.PARSER_GETOBJECT_ERR(currentEvent)); + } + return getObject(new JsonObjectBuilderImpl(bufferPool)); + } + + @Override + public JsonValue getValue() { + switch (currentEvent) { + case START_ARRAY: + return getArray(new JsonArrayBuilderImpl(bufferPool)); + case START_OBJECT: + return getObject(new JsonObjectBuilderImpl(bufferPool)); + case KEY_NAME: + case VALUE_STRING: + return new JsonStringImpl(getString()); + case VALUE_NUMBER: + if (isDefinitelyInt()) { + return JsonNumberImpl.getJsonNumber(getInt()); + } else if (isDefinitelyLong()) { + return JsonNumberImpl.getJsonNumber(getLong()); + } + return JsonNumberImpl.getJsonNumber(getBigDecimal()); + case VALUE_TRUE: + return JsonValue.TRUE; + case VALUE_FALSE: + return JsonValue.FALSE; + case VALUE_NULL: + return JsonValue.NULL; + case END_ARRAY: + case END_OBJECT: + default: + throw new IllegalStateException(JsonMessages.PARSER_GETVALUE_ERR(currentEvent)); + } + } + + @Override + public Stream getArrayStream() { + if (currentEvent != Event.START_ARRAY) { + throw new IllegalStateException( + JsonMessages.PARSER_GETARRAY_ERR(currentEvent)); + } + Spliterator spliterator = + new Spliterators.AbstractSpliterator(Long.MAX_VALUE, Spliterator.ORDERED) { + @Override + public Spliterator trySplit() { + return null; + } + @Override + public boolean tryAdvance(Consumer action) { + if (action == null) { + throw new NullPointerException(); + } + if (! hasNext()) { + return false; + } + if (next() == JsonParser.Event.END_ARRAY) { + return false; + } + action.accept(getValue()); + return true; + } + }; + return StreamSupport.stream(spliterator, false); + } + + @Override + public Stream> getObjectStream() { + if (currentEvent != Event.START_OBJECT) { + throw new IllegalStateException( + JsonMessages.PARSER_GETOBJECT_ERR(currentEvent)); + } + Spliterator> spliterator = + new Spliterators.AbstractSpliterator>(Long.MAX_VALUE, Spliterator.ORDERED) { + @Override + public Spliterator> trySplit() { + return null; + } + @Override + public boolean tryAdvance(Consumer> action) { + if (action == null) { + throw new NullPointerException(); + } + if (! hasNext()) { + return false; + } + JsonParser.Event e = next(); + if (e == JsonParser.Event.END_OBJECT) { + return false; + } + if (e != JsonParser.Event.KEY_NAME) { + throw new JsonException(JsonMessages.INTERNAL_ERROR()); + } + String key = getString(); + if (! hasNext()) { + throw new JsonException(JsonMessages.INTERNAL_ERROR()); + } + next(); + JsonValue value = getValue(); + action.accept(new AbstractMap.SimpleImmutableEntry<>(key, value)); + return true; + } + }; + return StreamSupport.stream(spliterator, false); + } + + @Override + public Stream getValueStream() { + if (! (currentContext instanceof NoneContext)) { + throw new IllegalStateException( + JsonMessages.PARSER_GETVALUESTREAM_ERR()); + } + Spliterator spliterator = + new Spliterators.AbstractSpliterator(Long.MAX_VALUE, Spliterator.ORDERED) { + @Override + public Spliterator trySplit() { + return null; + } + @Override + public boolean tryAdvance(Consumer action) { + if (action == null) { + throw new NullPointerException(); + } + if (! hasNext()) { + return false; + } + next(); + action.accept(getValue()); + return true; + } + }; + return StreamSupport.stream(spliterator, false); + } + + @Override + public void skipArray() { + if (currentEvent == Event.START_ARRAY) { + currentContext.skip(); + currentContext = stack.pop(); + } + } + + @Override + public void skipObject() { + if (currentEvent == Event.START_OBJECT) { + currentContext.skip(); + currentContext = stack.pop(); + } + } + + private JsonArray getArray(JsonArrayBuilder builder) { + while(hasNext()) { + JsonParser.Event e = next(); + if (e == JsonParser.Event.END_ARRAY) { + return builder.build(); + } + builder.add(getValue()); + } + throw parsingException(JsonToken.EOF, "[CURLYOPEN, SQUAREOPEN, STRING, NUMBER, TRUE, FALSE, NULL, SQUARECLOSE]"); + } + + private JsonObject getObject(JsonObjectBuilder builder) { + while(hasNext()) { + JsonParser.Event e = next(); + if (e == JsonParser.Event.END_OBJECT) { + return builder.build(); + } + String key = getString(); + next(); + builder.add(key, getValue()); + } + throw parsingException(JsonToken.EOF, "[STRING, CURLYCLOSE]"); + } + + @Override + public JsonLocation getLocation() { + return tokenizer.getLocation(); + } + + public JsonLocation getLastCharLocation() { + return tokenizer.getLastCharLocation(); + } + + @Override + public boolean hasNext() { + return tokenizer.hasNextToken(); + } + + @Override + public Event next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return currentEvent = currentContext.getNextEvent(); + } + + @Override + public void close() { + try { + tokenizer.close(); + } catch (IOException e) { + throw new JsonException(JsonMessages.PARSER_TOKENIZER_CLOSE_IO(), e); + } + } + + // Using the optimized stack impl as we don't require other things + // like iterator etc. + private static final class Stack { + private Context head; + + private void push(Context context) { + context.next = head; + head = context; + } + + private Context pop() { + if (head == null) { + throw new NoSuchElementException(); + } + Context temp = head; + head = head.next; + return temp; + } + + private Context peek() { + return head; + } + + private boolean isEmpty() { + return head == null; + } + } + + private abstract class Context { + Context next; + abstract Event getNextEvent(); + abstract void skip(); + } + + private final class NoneContext extends Context { + @Override + public Event getNextEvent() { + // Handle 1. { 2. [ 3. value + JsonToken token = tokenizer.nextToken(); + if (token == JsonToken.CURLYOPEN) { + stack.push(currentContext); + currentContext = new ObjectContext(); + return Event.START_OBJECT; + } else if (token == JsonToken.SQUAREOPEN) { + stack.push(currentContext); + currentContext = new ArrayContext(); + return Event.START_ARRAY; + } else if (token.isValue()) { + return token.getEvent(); + } + throw parsingException(token, "[CURLYOPEN, SQUAREOPEN, STRING, NUMBER, TRUE, FALSE, NULL]"); + } + + @Override + void skip() { + // no-op + } + } + + private JsonParsingException parsingException(JsonToken token, String expectedTokens) { + JsonLocation location = getLastCharLocation(); + return new JsonParsingException( + JsonMessages.PARSER_INVALID_TOKEN(token, location, expectedTokens), location); + } + + private final class ObjectContext extends Context { + private boolean firstValue = true; + + /* + * Some more things could be optimized. For example, instead + * tokenizer.nextToken(), one could use tokenizer.matchColonToken() to + * match ':'. That might optimize a bit, but will fragment nextToken(). + * I think the current one is more readable. + * + */ + @Override + public Event getNextEvent() { + // Handle 1. } 2. name:value 3. ,name:value + JsonToken token = tokenizer.nextToken(); + if (currentEvent == Event.KEY_NAME) { + // Handle 1. :value + if (token != JsonToken.COLON) { + throw parsingException(token, "[COLON]"); + } + token = tokenizer.nextToken(); + if (token.isValue()) { + return token.getEvent(); + } else if (token == JsonToken.CURLYOPEN) { + stack.push(currentContext); + currentContext = new ObjectContext(); + return Event.START_OBJECT; + } else if (token == JsonToken.SQUAREOPEN) { + stack.push(currentContext); + currentContext = new ArrayContext(); + return Event.START_ARRAY; + } + throw parsingException(token, "[CURLYOPEN, SQUAREOPEN, STRING, NUMBER, TRUE, FALSE, NULL]"); + } else { + // Handle 1. } 2. name 3. ,name + if (token == JsonToken.CURLYCLOSE) { + currentContext = stack.pop(); + return Event.END_OBJECT; + } + if (firstValue) { + firstValue = false; + } else { + if (token != JsonToken.COMMA) { + throw parsingException(token, "[COMMA]"); + } + token = tokenizer.nextToken(); + } + if (token == JsonToken.STRING) { + return Event.KEY_NAME; + } + throw parsingException(token, "[STRING]"); + } + } + + @Override + void skip() { + JsonToken token; + int depth = 1; + do { + token = tokenizer.nextToken(); + switch (token) { + case CURLYCLOSE: + depth--; + break; + case CURLYOPEN: + depth++; + break; + } + } while (!(token == JsonToken.CURLYCLOSE && depth == 0)); + } + + } + + private final class ArrayContext extends Context { + private boolean firstValue = true; + + // Handle 1. ] 2. value 3. ,value + @Override + public Event getNextEvent() { + JsonToken token = tokenizer.nextToken(); + if (token == JsonToken.SQUARECLOSE) { + currentContext = stack.pop(); + return Event.END_ARRAY; + } + if (firstValue) { + firstValue = false; + } else { + if (token != JsonToken.COMMA) { + throw parsingException(token, "[COMMA]"); + } + token = tokenizer.nextToken(); + } + if (token.isValue()) { + return token.getEvent(); + } else if (token == JsonToken.CURLYOPEN) { + stack.push(currentContext); + currentContext = new ObjectContext(); + return Event.START_OBJECT; + } else if (token == JsonToken.SQUAREOPEN) { + stack.push(currentContext); + currentContext = new ArrayContext(); + return Event.START_ARRAY; + } + throw parsingException(token, "[CURLYOPEN, SQUAREOPEN, STRING, NUMBER, TRUE, FALSE, NULL]"); + } + + @Override + void skip() { + JsonToken token; + int depth = 1; + do { + token = tokenizer.nextToken(); + switch (token) { + case SQUARECLOSE: + depth--; + break; + case SQUAREOPEN: + depth++; + break; + } + } while (!(token == JsonToken.SQUARECLOSE && depth == 0)); + } + } + +} diff --git a/impl/src/main/java/org/glassfish/json/JsonPatchBuilderImpl.java b/impl/src/main/java/org/glassfish/json/JsonPatchBuilderImpl.java new file mode 100644 index 00000000..862b01df --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonPatchBuilderImpl.java @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonArrayBuilder; +import javax.json.JsonException; +import javax.json.JsonPatch; +import javax.json.JsonPatch.Operation; +import javax.json.JsonPatchBuilder; +import javax.json.JsonStructure; +import javax.json.JsonValue; + +/** + * A builder for constructing a JSON Patch by adding + * JSON Patch operations incrementally. + *

+ * The following illustrates the approach. + *

+ *   JsonPatchBuilder builder = Json.createPatchBuilder();
+ *   JsonPatch patch = builder.add("/John/phones/office", "1234-567")
+ *                            .remove("/Amy/age")
+ *                            .build();
+ * 
+ * The result is equivalent to the following JSON Patch. + *
+ * [
+ *    {"op" = "add", "path" = "/John/phones/office", "value" = "1234-567"},
+ *    {"op" = "remove", "path" = "/Amy/age"}
+ * ] 
+ * + * @since 1.1 + */ +public final class JsonPatchBuilderImpl implements JsonPatchBuilder { + + private final JsonArrayBuilder builder; + + /** + * Creates a JsonPatchBuilderImpl, starting with the specified + * JSON Patch + * @param patch the JSON Patch + */ + public JsonPatchBuilderImpl(JsonArray patch) { + builder = Json.createArrayBuilder(patch); + } + + /** + * Creates JsonPatchBuilderImpl with empty JSON Patch + */ + public JsonPatchBuilderImpl() { + builder = Json.createArrayBuilder(); + } + + /** + * A convenience method for {@code new JsonPatchImpl(build()).apply(target)}. + * The target is not modified by the patch. + * + * @param the target type, must be a subtype of {@link JsonStructure} + * @param target the target to apply the patch operations + * @return the transformed target after the patch + * @throws JsonException if the supplied JSON Patch is malformed or if + * it contains references to non-existing members + */ + public T apply(T target) { + return build().apply(target); + } + + /** + * Adds an "add" JSON Patch operation. + * @param path the "path" member of the operation + * @param value the "value" member of the operation + * @return this JsonPatchBuilder + */ + @Override + public JsonPatchBuilder add(String path, JsonValue value) { + builder.add(Json.createObjectBuilder() + .add("op", Operation.ADD.operationName()) + .add("path", path) + .add("value", value) + ); + return this; + } + + /** + * Adds an "add" JSON Patch operation + * @param path the "path" member of the operation + * @param value the "value" member of the operation + * @return this JsonPatchBuilder + */ + @Override + public JsonPatchBuilder add(String path, String value) { + builder.add(Json.createObjectBuilder() + .add("op", Operation.ADD.operationName()) + .add("path", path) + .add("value", value) + ); + return this; + } + + /** + * Adds an "add" JSON Patch operation + * @param path the "path" member of the operation + * @param value the "value" member of the operation + * @return this JsonPatchBuilder + */ + @Override + public JsonPatchBuilder add(String path, int value) { + builder.add(Json.createObjectBuilder() + .add("op", Operation.ADD.operationName()) + .add("path", path) + .add("value", value) + ); + return this; + } + + /** + * Adds an "add" JSON Patch operation + * @param path the "path" member of the operation + * @param value the "value" member of the operation + * @return this JsonPatchBuilder + */ + @Override + public JsonPatchBuilder add(String path, boolean value) { + builder.add(Json.createObjectBuilder() + .add("op", Operation.ADD.operationName()) + .add("path", path) + .add("value", value) + ); + return this; + } + + /** + * Adds a "remove" JSON Patch operation. + * @param path the "path" member of the operation + * @return this JsonPatchBuilder + */ + @Override + public JsonPatchBuilder remove(String path) { + builder.add(Json.createObjectBuilder() + .add("op", Operation.REMOVE.operationName()) + .add("path", path) + ); + return this; + } + + /** + * Adds a "replace" JSON Patch operation. + * @param path the "path" member of the operation + * @param value the "value" member of the operation + * @return this JsonPatchBuilder + */ + @Override + public JsonPatchBuilder replace(String path, JsonValue value) { + builder.add(Json.createObjectBuilder() + .add("op", Operation.REPLACE.operationName()) + .add("path", path) + .add("value", value) + ); + return this; + } + + /** + * Adds a "replace" JSON Patch operation. + * @param path the "path" member of the operation + * @param value the "value" member of the operation + * @return this JsonPatchBuilder + */ + @Override + public JsonPatchBuilder replace(String path, String value) { + builder.add(Json.createObjectBuilder() + .add("op", Operation.REPLACE.operationName()) + .add("path", path) + .add("value", value) + ); + return this; + } + + /** + * Adds a "replace" JSON Patch operation. + * @param path the "path" member of the operation + * @param value the "value" member of the operation + * @return this JsonPatchBuilder + */ + @Override + public JsonPatchBuilder replace(String path, int value) { + builder.add(Json.createObjectBuilder() + .add("op", Operation.REPLACE.operationName()) + .add("path", path) + .add("value", value) + ); + return this; + } + + /** + * Adds a "replace" JSON Patch operation. + * @param path the "path" member of the operation + * @param value the "value" member of the operation + * @return this JsonPatchBuilder + */ + @Override + public JsonPatchBuilder replace(String path, boolean value) { + builder.add(Json.createObjectBuilder() + .add("op", Operation.REPLACE.operationName()) + .add("path", path) + .add("value", value) + ); + return this; + } + + /** + * Adds a "move" JSON Patch operation. + * @param path the "path" member of the operation + * @param from the "from" member of the operation + * @return this JsonPatchBuilder + */ + @Override + public JsonPatchBuilder move(String path, String from) { + builder.add(Json.createObjectBuilder() + .add("op", Operation.MOVE.operationName()) + .add("path", path) + .add("from", from) + ); + return this; + } + + /** + * Adds a "copy" JSON Patch operation. + * @param path the "path" member of the operation + * @param from the "from" member of the operation + * @return this JsonPatchBuilder + */ + @Override + public JsonPatchBuilder copy(String path, String from) { + builder.add(Json.createObjectBuilder() + .add("op", Operation.COPY.operationName()) + .add("path", path) + .add("from", from) + ); + return this; + } + + /** + * Adds a "test" JSON Patch operation. + * @param path the "path" member of the operation + * @param value the "value" member of the operation + * @return this JsonPatchBuilder + */ + @Override + public JsonPatchBuilder test(String path, JsonValue value) { + builder.add(Json.createObjectBuilder() + .add("op", Operation.TEST.operationName()) + .add("path", path) + .add("value", value) + ); + return this; + } + + /** + * Adds a "test" JSON Patch operation. + * @param path the "path" member of the operation + * @param value the "value" member of the operation + * @return this JsonPatchBuilder + */ + @Override + public JsonPatchBuilder test(String path, String value) { + builder.add(Json.createObjectBuilder() + .add("op", Operation.TEST.operationName()) + .add("path", path) + .add("value", value) + ); + return this; + } + + /** + * Adds a "test" JSON Patch operation. + * @param path the "path" member of the operation + * @param value the "value" member of the operation + * @return this JsonPatchBuilder + */ + @Override + public JsonPatchBuilder test(String path, int value) { + builder.add(Json.createObjectBuilder() + .add("op", Operation.TEST.operationName()) + .add("path", path) + .add("value", value) + ); + return this; + } + + /** + * Adds a "test" JSON Patch operation. + * @param path the "path" member of the operation + * @param value the "value" member of the operation + * @return this JsonPatchBuilder + */ + @Override + public JsonPatchBuilder test(String path, boolean value) { + builder.add(Json.createObjectBuilder() + .add("op", Operation.TEST.operationName()) + .add("path", path) + .add("value", value) + ); + return this; + } + + /** + * Returns the patch operations in a JsonArray + * @return the patch operations in a JsonArray + */ + public JsonArray buildAsJsonArray() { + return builder.build(); + } + + /** + * Returns the patch operation in a JsonPatch + * @return the patch operation in a JsonPatch + */ + @Override + public JsonPatch build() { + return new JsonPatchImpl(buildAsJsonArray()); + } +} + diff --git a/impl/src/main/java/org/glassfish/json/JsonPatchImpl.java b/impl/src/main/java/org/glassfish/json/JsonPatchImpl.java new file mode 100644 index 00000000..fa641425 --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonPatchImpl.java @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import javax.json.*; +import javax.json.JsonValue.ValueType; + +/** + * This class is an immutable representation of a JSON Patch as specified in + * RFC 6902. + *

A {@code JsonPatch} can be instantiated with {@link Json#createPatch(JsonArray)} + * by specifying the patch operations in a JSON Patch. Alternately, it + * can also be constructed with a {@link JsonPatchBuilder}. + *

+ * The following illustrates both approaches. + *

1. Construct a JsonPatch with a JSON Patch. + *

{@code
+ *   JsonArray contacts = ... // The target to be patched
+ *   JsonArray patch = ...  ; // JSON Patch
+ *   JsonPatch jsonpatch = Json.createPatch(patch);
+ *   JsonArray result = jsonpatch.apply(contacts);
+ * } 
+ * 2. Construct a JsonPatch with JsonPatchBuilder. + *
{@code
+ *   JsonPatchBuilder builder = Json.createPatchBuilder();
+ *   JsonArray result = builder.add("/John/phones/office", "1234-567")
+ *                             .remove("/Amy/age")
+ *                             .build()
+ *                             .apply(contacts);
+ * } 
+ * + * @since 1.1 + */ +public class JsonPatchImpl implements JsonPatch { + + private final JsonArray patch; + + /** + * Constructs a JsonPatchImpl + * @param patch the JSON Patch + */ + public JsonPatchImpl(JsonArray patch) { + this.patch = patch; + } + + /** + * Compares this {@code JsonPatchImpl} with another object. + * @param obj the object to compare this {@code JsonPatchImpl} against + * @return true if the given object is a {@code JsonPatchImpl} with the same + * reference tokens as this one, false otherwise. + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || obj.getClass() != JsonPatchImpl.class) + return false; + return patch.equals(((JsonPatchImpl)obj).patch); + } + + /** + * Returns the hash code value for this {@code JsonPatchImpl}. + * + * @return the hash code value for this {@code JsonPatchImpl} object + */ + @Override + public int hashCode() { + return patch.hashCode(); + } + + /** + * Returns the JSON Patch text + * @return the JSON Patch text + */ + @Override + public String toString() { + return patch.toString(); + } + + /** + * Applies the patch operations to the specified {@code target}. + * The target is not modified by the patch. + * + * @param target the target to apply the patch operations + * @return the transformed target after the patch + * @throws JsonException if the supplied JSON Patch is malformed or if + * it contains references to non-existing members + */ + @Override + public JsonStructure apply(JsonStructure target) { + + JsonStructure result = target; + + for (JsonValue operation: patch) { + if (operation.getValueType() != ValueType.OBJECT) { + throw new JsonException(JsonMessages.PATCH_MUST_BE_ARRAY()); + } + result = apply(result, (JsonObject) operation); + } + return result; + } + + @Override + public JsonArray toJsonArray() { + return patch; + } + + /** + * Generates a JSON Patch from the source and target {@code JsonStructure}. + * The generated JSON Patch need not be unique. + * @param source the source + * @param target the target, must be the same type as the source + * @return a JSON Patch which when applied to the source, yields the target + */ + public static JsonArray diff(JsonStructure source, JsonStructure target) { + return (new DiffGenerator()).diff(source, target); + } + + /** + * Applies a JSON Patch operation to the target. + * @param target the target to apply the operation + * @param operation the JSON Patch operation + * @return the target after the patch + */ + private JsonStructure apply(JsonStructure target, JsonObject operation) { + + JsonPointer pointer = getPointer(operation, "path"); + JsonPointer from; + switch (Operation.fromOperationName(operation.getString("op"))) { + case ADD: + return pointer.add(target, getValue(operation)); + case REPLACE: + return pointer.replace(target, getValue(operation)); + case REMOVE: + return pointer.remove(target); + case COPY: + from = getPointer(operation, "from"); + return pointer.add(target, from.getValue(target)); + case MOVE: + // Check if from is a proper prefix of path + String dest = operation.getString("path"); + String src = operation.getString("from"); + if (dest.startsWith(src) && src.length() < dest.length()) { + throw new JsonException(JsonMessages.PATCH_MOVE_PROPER_PREFIX(src, dest)); + } + from = getPointer(operation, "from"); + // Check if 'from' exists in target object + if (!from.containsValue(target)) { + throw new JsonException(JsonMessages.PATCH_MOVE_TARGET_NULL(src)); + } + if (pointer.equals(from)) { + // nop + return target; + } + return pointer.add(from.remove(target), from.getValue(target)); + case TEST: + if (! getValue(operation).equals(pointer.getValue(target))) { + throw new JsonException(JsonMessages.PATCH_TEST_FAILED(operation.getString("path"), getValue(operation).toString())); + } + return target; + default: + throw new JsonException(JsonMessages.PATCH_ILLEGAL_OPERATION(operation.getString("op"))); + } + } + + private JsonPointer getPointer(JsonObject operation, String member) { + JsonString pointerString = operation.getJsonString(member); + if (pointerString == null) { + missingMember(operation.getString("op"), member); + } + return Json.createPointer(pointerString.getString()); + } + + private JsonValue getValue(JsonObject operation) { + JsonValue value = operation.get("value"); + if (value == null) { + missingMember(operation.getString("op"), "value"); + } + return value; + } + + private void missingMember(String op, String member) { + throw new JsonException(JsonMessages.PATCH_MEMBER_MISSING(op, member)); + } + + static class DiffGenerator { + private JsonPatchBuilder builder; + + JsonArray diff(JsonStructure source, JsonStructure target) { + builder = Json.createPatchBuilder(); + diff("", source, target); + return builder.build().toJsonArray(); + } + + private void diff(String path, JsonValue source, JsonValue target) { + if (source.equals(target)) { + return; + } + ValueType s = source.getValueType(); + ValueType t = target.getValueType(); + if (s == ValueType.OBJECT && t == ValueType.OBJECT) { + diffObject(path, (JsonObject) source, (JsonObject) target); + } else if (s == ValueType.ARRAY && t == ValueType.ARRAY) { + diffArray(path, (JsonArray) source, (JsonArray) target); + } else { + builder.replace(path, target); + } + } + + private void diffObject(String path, JsonObject source, JsonObject target) { + source.forEach((key, value) -> { + if (target.containsKey(key)) { + diff(path + '/' + key, value, target.get(key)); + } else { + builder.remove(path + '/' + key); + } + }); + target.forEach((key, value) -> { + if (! source.containsKey(key)) { + builder.add(path + '/' + key, value); + } + }); + } + + /* + * For array element diff, find the longest common subsequence, per + * http://en.wikipedia.org/wiki/Longest_common_subsequence_problem . + * We modify the algorithm to generate a replace if possible. + */ + private void diffArray(String path, JsonArray source, JsonArray target) { + /* The array c keeps track of length of the subsequence. To avoid + * computing the equality of array elements again, we + * left shift its value by 1, and use the low order bit to mark + * that two items are equal. + */ + int m = source.size(); + int n = target.size(); + int [][] c = new int[m+1][n+1]; + for (int i = 0; i < m+1; i++) + c[i][0] = 0; + for (int i = 0; i < n+1; i++) + c[0][i] = 0; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (source.get(i).equals(target.get(j))) { + c[i+1][j+1] = ((c[i][j]) & ~1) + 3; + // 3 = (1 << 1) | 1; + } else { + c[i+1][j+1] = Math.max(c[i+1][j], c[i][j+1]) & ~1; + } + } + } + + emit(path, source, target, c, m, n); + } + + private void emit(final String path, + final JsonArray source, + final JsonArray target, + final int[][] c, + final int i, + final int j) { + if (i == 0) { + if (j > 0) { + emit(path, source, target, c, i, j - 1); + builder.add(path + '/' + (j - 1), target.get(j - 1)); + } + } else if (j == 0) { + if (i > 0) { + builder.remove(path + '/' + (i - 1)); + emit(path, source, target, c, i - 1, j); + } + } else if ((c[i][j] & 1) == 1) { + emit(path, source, target, c, i - 1, j - 1); + } else { + final int f = c[i][j-1] >> 1; + final int g = c[i-1][j] >> 1; + if (f > g) { + emit(path, source, target, c, i, j - 1); + builder.add(path + '/' + (j - 1), target.get(j - 1)); + } else if (f < g) { + builder.remove(path + '/' + (i - 1)); + emit(path, source, target, c, i - 1, j); + } else { // f == g) { + diff(path + '/' + (i - 1), source.get(i - 1), + target.get(j - 1)); + emit(path, source, target, c, i - 1, j - 1); + } + } + } + } +} + diff --git a/impl/src/main/java/org/glassfish/json/JsonPointerImpl.java b/impl/src/main/java/org/glassfish/json/JsonPointerImpl.java new file mode 100644 index 00000000..26b56971 --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonPointerImpl.java @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import javax.json.JsonArray; +import javax.json.JsonException; +import javax.json.JsonObject; +import javax.json.JsonPointer; +import javax.json.JsonStructure; +import javax.json.JsonValue; +import java.io.Serializable; +import java.util.function.BiFunction; + +/** + *

This class is an immutable representation of a JSON Pointer as specified in + * RFC 6901. + *

+ *

A JSON Pointer, when applied to a target {@link JsonValue}, + * defines a reference location in the target.

+ *

An empty JSON Pointer string defines a reference to the target itself.

+ *

If the JSON Pointer string is non-empty, it must be a sequence + * of '/' prefixed tokens, and the target must either be a {@link JsonArray} + * or {@link JsonObject}. If the target is a {@code JsonArray}, the pointer + * defines a reference to an array element, and the last token specifies the index. + * If the target is a {@link JsonObject}, the pointer defines a reference to a + * name/value pair, and the last token specifies the name. + *

+ *

The method {@link #getValue getValue()} returns the referenced value. + * The methods {@link #add add()}, {@link #replace replace()}, + * and {@link #remove remove()} executes the operations specified in + * RFC 6902.

+ * + * @since 1.1 + */ + +public final class JsonPointerImpl implements JsonPointer, Serializable { + + private static final long serialVersionUID = -8123110179640843141L; + private final String[] tokens; + private final String jsonPointer; + + /** + * Constructs and initializes a JsonPointerImpl. + * @param jsonPointer the JSON Pointer string + * @throws NullPointerException if {@code jsonPointer} is {@code null} + * @throws JsonException if {@code jsonPointer} is not a valid JSON Pointer + */ + public JsonPointerImpl(String jsonPointer) { + this.jsonPointer = jsonPointer; + tokens = jsonPointer.split("/", -1); // keep the trailing blanks + if (! "".equals(tokens[0])) { + throw new JsonException(JsonMessages.POINTER_FORMAT_INVALID()); + } + for (int i = 1; i < tokens.length; i++) { + String token = tokens[i]; + StringBuilder reftoken = new StringBuilder(); + for (int j = 0; j < token.length(); j++) { + char ch = token.charAt(j); + if (ch == '~' && j < token.length() - 1) { + char ch1 = token.charAt(j+1); + if (ch1 == '0') { + ch = '~'; j++; + } else if (ch1 == '1') { + ch = '/'; j++; + } + } + reftoken.append(ch); + } + tokens[i] = reftoken.toString(); + } + } + + /** + * Compares this {@code JsonPointer} with another object. + * @param obj the object to compare this {@code JsonPointer} against + * @return true if the given object is a {@code JsonPointer} with the same + * reference tokens as this one, false otherwise. + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || obj.getClass() != JsonPointerImpl.class) + return false; + return jsonPointer.equals(((JsonPointerImpl)obj).jsonPointer); + } + + /** + * Returns the hash code value for this {@code JsonPointer} object. + * The hash code of this object is defined by the hash codes of it's reference tokens. + * + * @return the hash code value for this {@code JsonPointer} object + */ + @Override + public int hashCode() { + return jsonPointer.hashCode(); + } + + /** + * Returns {@code true} if there is a value at the referenced location in the specified {@code target}. + * + * @param target the target referenced by this {@code JsonPointer} + * @return {@code true} if this pointer points to a value in a specified {@code target}. + */ + @Override + public boolean containsValue(JsonStructure target) { + NodeReference[] refs = getReferences(target); + return refs[0].contains(); + } + + /** + * Returns the value at the referenced location in the specified {@code target} + * + * @param target the target referenced by this {@code JsonPointer} + * @return the referenced value in the target. + * @throws NullPointerException if {@code target} is null + * @throws JsonException if the referenced value does not exist + */ + @Override + public JsonValue getValue(JsonStructure target) { + NodeReference[] refs = getReferences(target); + return refs[0].get(); + } + + /** + * Adds or replaces a value at the referenced location in the specified + * {@code target} with the specified {@code value}. + *
    + *
  1. If the reference is the target (empty JSON Pointer string), + * the specified {@code value}, which must be the same type as + * specified {@code target}, is returned.
  2. + *
  3. If the reference is an array element, the specified {@code value} is inserted + * into the array, at the referenced index. The value currently at that location, and + * any subsequent values, are shifted to the right (adds one to the indices). + * Index starts with 0. If the reference is specified with a "-", or if the + * index is equal to the size of the array, the value is appended to the array.
  4. + *
  5. If the reference is a name/value pair of a {@code JsonObject}, and the + * referenced value exists, the value is replaced by the specified {@code value}. + * If the value does not exist, a new name/value pair is added to the object.
  6. + *
+ * + * @param target the target referenced by this {@code JsonPointer} + * @param value the value to be added + * @return the transformed {@code target} after the value is added. + * @throws NullPointerException if {@code target} is {@code null} + * @throws JsonException if the reference is an array element and + * the index is out of range ({@code index < 0 || index > array size}), + * or if the pointer contains references to non-existing objects or arrays. + */ + @Override + public JsonStructure add(JsonStructure target, JsonValue value) { + return execute(NodeReference::add, target, value); + } + + /** + * Replaces the value at the referenced location in the specified + * {@code target} with the specified {@code value}. + * + * @param target the target referenced by this {@code JsonPointer} + * @param value the value to be stored at the referenced location + * @return the transformed {@code target} after the value is replaced. + * @throws NullPointerException if {@code target} is {@code null} + * @throws JsonException if the referenced value does not exist, + * or if the reference is the target. + */ + @Override + public JsonStructure replace(JsonStructure target, JsonValue value) { + return execute(NodeReference::replace, target, value); + } + + /** + * Removes the value at the reference location in the specified {@code target} + * + * @param target the target referenced by this {@code JsonPointer} + * @return the transformed {@code target} after the value is removed. + * @throws NullPointerException if {@code target} is {@code null} + * @throws JsonException if the referenced value does not exist, + * or if the reference is the target. + */ + @Override + public JsonStructure remove(JsonStructure target) { + return execute((r,v)->r.remove(), target, null); + } + + /** + * Executes the operation + * @param op a {code BiFunction} used to specify the operation to execute on + * the leaf node of the Json Pointer + * @param target the target JsonStructure for this JsonPointer + * @param value the JsonValue for add and replace, can be null for getvalue and remove + */ + private JsonStructure execute(BiFunction op, + JsonStructure target, JsonValue value) { + + NodeReference[] refs = getReferences(target); + JsonStructure result = op.apply(refs[0], value); + for (int i = 1; i < refs.length; i++) { + result = refs[i].replace(result); + } + return result; + } + + /** + * Computes the {@code NodeReference}s for each node on the path of + * the JSON Pointer, in reverse order, starting from the leaf node + */ + private NodeReference[] getReferences(JsonStructure target) { + NodeReference[] references; + // First check if this is a reference to a JSON value tree + if (tokens.length == 1) { + references = new NodeReference[1]; + references[0] = NodeReference.of(target); + return references; + } + + references = new NodeReference[tokens.length-1]; + JsonValue value = target; + int s = tokens.length; + for (int i = 1; i < s; i++) { + // Start with index 1, skipping the "" token + switch (value.getValueType()) { + case OBJECT: + JsonObject object = (JsonObject) value; + references[s-i-1] = NodeReference.of(object, tokens[i]); + if (i < s-1) { + value = object.get(tokens[i]); + if (value == null) { + // Except for the last name, the mapping must exist + throw new JsonException(JsonMessages.POINTER_MAPPING_MISSING(object, tokens[i])); + } + } + break; + case ARRAY: + int index = getIndex(tokens[i]); + JsonArray array = (JsonArray) value; + references[s-i-1] = NodeReference.of(array, index); + if (i < s-1 && index != -1) { + if (index >= array.size()) { + throw new JsonException(JsonMessages.NODEREF_ARRAY_INDEX_ERR(index, array.size())); + } + // The last array index in the path can have index value of -1 + // ("-" in the JSON pointer) + value = array.get(index); + } + break; + default: + throw new JsonException(JsonMessages.POINTER_REFERENCE_INVALID(value.getValueType())); + } + } + return references; + } + + /** + * Computes the array index + * @param token the input string token + * @return the array index. -1 if the token is "-" + * @throws JsonException if the string token is not in correct format + */ + static private int getIndex(String token) { + if (token == null || token.length() == 0) { + throw new JsonException(JsonMessages.POINTER_ARRAY_INDEX_ERR(token)); + } + if (token.equals("-")) { + return -1; + } + if (token.equals("0")) { + return 0; + } + if (token.charAt(0) == '+' || token.charAt(0) == '-') { + throw new JsonException(JsonMessages.POINTER_ARRAY_INDEX_ERR(token)); + } + try { + return Integer.parseInt(token); + } catch (NumberFormatException ex) { + throw new JsonException(JsonMessages.POINTER_ARRAY_INDEX_ILLEGAL(token), ex); + } + } +} diff --git a/impl/src/main/java/org/glassfish/json/JsonPrettyGeneratorImpl.java b/impl/src/main/java/org/glassfish/json/JsonPrettyGeneratorImpl.java new file mode 100644 index 00000000..c862fabc --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonPrettyGeneratorImpl.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import org.glassfish.json.api.BufferPool; + +import javax.json.stream.JsonGenerator; +import java.io.OutputStream; +import java.io.Writer; +import java.nio.charset.Charset; + +/** + * @author Jitendra Kotamraju + */ +public class JsonPrettyGeneratorImpl extends JsonGeneratorImpl { + private int indentLevel; + private static final String INDENT = " "; + + public JsonPrettyGeneratorImpl(Writer writer, BufferPool bufferPool) { + super(writer, bufferPool); + } + + public JsonPrettyGeneratorImpl(OutputStream out, BufferPool bufferPool) { + super(out, bufferPool); + } + + public JsonPrettyGeneratorImpl(OutputStream out, Charset encoding, BufferPool bufferPool) { + super(out, encoding, bufferPool); + } + + @Override + public JsonGenerator writeStartObject() { + super.writeStartObject(); + indentLevel++; + return this; + } + + @Override + public JsonGenerator writeStartObject(String name) { + super.writeStartObject(name); + indentLevel++; + return this; + } + + @Override + public JsonGenerator writeStartArray() { + super.writeStartArray(); + indentLevel++; + return this; + } + + @Override + public JsonGenerator writeStartArray(String name) { + super.writeStartArray(name); + indentLevel++; + return this; + } + + @Override + public JsonGenerator writeEnd() { + writeNewLine(); + indentLevel--; + writeIndent(); + super.writeEnd(); + return this; + } + + private void writeIndent() { + for(int i=0; i < indentLevel; i++) { + writeString(INDENT); + } + } + + @Override + protected void writeComma() { + super.writeComma(); + writeChar('\n'); + writeIndent(); + } + + @Override + protected void writeColon() { + super.writeColon(); + writeChar(' '); + } + + private void writeNewLine() { + writeChar('\n'); + } +} diff --git a/impl/src/main/java/org/glassfish/json/JsonProviderImpl.java b/impl/src/main/java/org/glassfish/json/JsonProviderImpl.java new file mode 100644 index 00000000..8cd623e0 --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonProviderImpl.java @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import org.glassfish.json.api.BufferPool; + +import javax.json.*; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonGeneratorFactory; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParserFactory; +import javax.json.spi.JsonProvider; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.math.BigDecimal; +import java.math.BigInteger; + +/** + * @author Jitendra Kotamraju + * @author Kin-man Chung + * @author Alex Soto + */ +public class JsonProviderImpl extends JsonProvider { + + private final BufferPool bufferPool = new BufferPoolImpl(); + + @Override + public JsonGenerator createGenerator(Writer writer) { + return new JsonGeneratorImpl(writer, bufferPool); + } + + @Override + public JsonGenerator createGenerator(OutputStream out) { + return new JsonGeneratorImpl(out, bufferPool); + } + + @Override + public JsonParser createParser(Reader reader) { + return new JsonParserImpl(reader, bufferPool); + } + + @Override + public JsonParser createParser(InputStream in) { + return new JsonParserImpl(in, bufferPool); + } + + @Override + public JsonParserFactory createParserFactory(Map config) { + BufferPool pool = null; + if (config != null && config.containsKey(BufferPool.class.getName())) { + pool = (BufferPool)config.get(BufferPool.class.getName()); + } + if (pool == null) { + pool = bufferPool; + } + return new JsonParserFactoryImpl(pool); + } + + @Override + public JsonGeneratorFactory createGeneratorFactory(Map config) { + Map providerConfig; + boolean prettyPrinting; + BufferPool pool; + if (config == null) { + providerConfig = Collections.emptyMap(); + prettyPrinting = false; + pool = bufferPool; + } else { + providerConfig = new HashMap<>(); + if (prettyPrinting=JsonProviderImpl.isPrettyPrintingEnabled(config)) { + providerConfig.put(JsonGenerator.PRETTY_PRINTING, true); + } + pool = (BufferPool)config.get(BufferPool.class.getName()); + if (pool != null) { + providerConfig.put(BufferPool.class.getName(), pool); + } else { + pool = bufferPool; + } + providerConfig = Collections.unmodifiableMap(providerConfig); + } + + return new JsonGeneratorFactoryImpl(providerConfig, prettyPrinting, pool); + } + + @Override + public JsonReader createReader(Reader reader) { + return new JsonReaderImpl(reader, bufferPool); + } + + @Override + public JsonReader createReader(InputStream in) { + return new JsonReaderImpl(in, bufferPool); + } + + @Override + public JsonWriter createWriter(Writer writer) { + return new JsonWriterImpl(writer, bufferPool); + } + + @Override + public JsonWriter createWriter(OutputStream out) { + return new JsonWriterImpl(out, bufferPool); + } + + @Override + public JsonWriterFactory createWriterFactory(Map config) { + Map providerConfig; + boolean prettyPrinting; + BufferPool pool; + if (config == null) { + providerConfig = Collections.emptyMap(); + prettyPrinting = false; + pool = bufferPool; + } else { + providerConfig = new HashMap<>(); + if (prettyPrinting=JsonProviderImpl.isPrettyPrintingEnabled(config)) { + providerConfig.put(JsonGenerator.PRETTY_PRINTING, true); + } + pool = (BufferPool)config.get(BufferPool.class.getName()); + if (pool != null) { + providerConfig.put(BufferPool.class.getName(), pool); + } else { + pool = bufferPool; + } + providerConfig = Collections.unmodifiableMap(providerConfig); + } + return new JsonWriterFactoryImpl(providerConfig, prettyPrinting, pool); + } + + @Override + public JsonReaderFactory createReaderFactory(Map config) { + BufferPool pool = null; + if (config != null && config.containsKey(BufferPool.class.getName())) { + pool = (BufferPool)config.get(BufferPool.class.getName()); + } + if (pool == null) { + pool = bufferPool; + } + return new JsonReaderFactoryImpl(pool); + } + + @Override + public JsonObjectBuilder createObjectBuilder() { + return new JsonObjectBuilderImpl(bufferPool); + } + + @Override + public JsonObjectBuilder createObjectBuilder(JsonObject object) { + return new JsonObjectBuilderImpl(object, bufferPool); + } + + @Override + public JsonObjectBuilder createObjectBuilder(Map map) { + return new JsonObjectBuilderImpl(map, bufferPool); + } + + @Override + public JsonArrayBuilder createArrayBuilder() { + return new JsonArrayBuilderImpl(bufferPool); + } + + @Override + public JsonArrayBuilder createArrayBuilder(JsonArray array) { + return new JsonArrayBuilderImpl(array, bufferPool); + } + + @Override + public JsonArrayBuilder createArrayBuilder(Collection collection) { + return new JsonArrayBuilderImpl(collection, bufferPool); + } + + @Override + public JsonPointer createPointer(String jsonPointer) { + return new JsonPointerImpl(jsonPointer); + } + + @Override + public JsonPatchBuilder createPatchBuilder() { + return new JsonPatchBuilderImpl(); + } + + @Override + public JsonPatchBuilder createPatchBuilder(JsonArray array) { + return new JsonPatchBuilderImpl(array); + } + + @Override + public JsonPatch createPatch(JsonArray array) { + return new JsonPatchImpl(array); + } + + @Override + public JsonPatch createDiff(JsonStructure source, JsonStructure target) { + return new JsonPatchImpl(JsonPatchImpl.diff(source, target)); + } + + @Override + public JsonMergePatch createMergePatch(JsonValue patch) { + return new JsonMergePatchImpl(patch); + } + + @Override + public JsonMergePatch createMergeDiff(JsonValue source, JsonValue target) { + return new JsonMergePatchImpl(JsonMergePatchImpl.diff(source, target)); + } + + @Override + public JsonString createValue(String value) { + return new JsonStringImpl(value); + } + + @Override + public JsonNumber createValue(int value) { + return JsonNumberImpl.getJsonNumber(value); + } + + @Override + public JsonNumber createValue(long value) { + return JsonNumberImpl.getJsonNumber(value); + } + + @Override + public JsonNumber createValue(double value) { + return JsonNumberImpl.getJsonNumber(value); + } + + @Override + public JsonNumber createValue(BigInteger value) { + return JsonNumberImpl.getJsonNumber(value); + } + + @Override + public JsonNumber createValue(BigDecimal value) { + return JsonNumberImpl.getJsonNumber(value); + } + + @Override + public JsonBuilderFactory createBuilderFactory(Map config) { + BufferPool pool = null ; + if (config != null && config.containsKey(BufferPool.class.getName())) { + pool = (BufferPool)config.get(BufferPool.class.getName()); + } + if (pool == null) { + pool = bufferPool; + } + return new JsonBuilderFactoryImpl(pool); + } + + static boolean isPrettyPrintingEnabled(Map config) { + return config.containsKey(JsonGenerator.PRETTY_PRINTING); + } +} diff --git a/impl/src/main/java/org/glassfish/json/JsonReaderFactoryImpl.java b/impl/src/main/java/org/glassfish/json/JsonReaderFactoryImpl.java new file mode 100644 index 00000000..50baf2f9 --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonReaderFactoryImpl.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import org.glassfish.json.api.BufferPool; + +import javax.json.JsonReader; +import javax.json.JsonReaderFactory; +import java.io.InputStream; +import java.io.Reader; +import java.nio.charset.Charset; +import java.util.Collections; +import java.util.Map; + +/** + * @author Jitendra Kotamraju + */ +class JsonReaderFactoryImpl implements JsonReaderFactory { + private final Map config = Collections.emptyMap(); + private final BufferPool bufferPool; + + JsonReaderFactoryImpl(BufferPool bufferPool) { + this.bufferPool = bufferPool; + } + + @Override + public JsonReader createReader(Reader reader) { + return new JsonReaderImpl(reader, bufferPool); + } + + @Override + public JsonReader createReader(InputStream in) { + return new JsonReaderImpl(in, bufferPool); + } + + @Override + public JsonReader createReader(InputStream in, Charset charset) { + return new JsonReaderImpl(in, charset, bufferPool); + } + + @Override + public Map getConfigInUse() { + return config; + } +} diff --git a/impl/src/main/java/org/glassfish/json/JsonReaderImpl.java b/impl/src/main/java/org/glassfish/json/JsonReaderImpl.java new file mode 100644 index 00000000..5517382f --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonReaderImpl.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import org.glassfish.json.api.BufferPool; + +import java.io.InputStream; +import java.io.Reader; +import java.nio.charset.Charset; +import javax.json.JsonArray; +import javax.json.JsonException; +import javax.json.JsonObject; +import javax.json.JsonReader; +import javax.json.JsonStructure; +import javax.json.JsonValue; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParsingException; + +/** + * JsonReader impl using parser and builders. + * + * @author Jitendra Kotamraju + */ +class JsonReaderImpl implements JsonReader { + private final JsonParserImpl parser; + private boolean readDone; + private final BufferPool bufferPool; + + JsonReaderImpl(Reader reader, BufferPool bufferPool) { + parser = new JsonParserImpl(reader, bufferPool); + this.bufferPool = bufferPool; + } + + JsonReaderImpl(InputStream in, BufferPool bufferPool) { + parser = new JsonParserImpl(in, bufferPool); + this.bufferPool = bufferPool; + } + + JsonReaderImpl(InputStream in, Charset charset, BufferPool bufferPool) { + parser = new JsonParserImpl(in, charset, bufferPool); + this.bufferPool = bufferPool; + } + + @Override + public JsonStructure read() { + if (readDone) { + throw new IllegalStateException(JsonMessages.READER_READ_ALREADY_CALLED()); + } + readDone = true; + if (parser.hasNext()) { + try { + JsonParser.Event e = parser.next(); + if (e == JsonParser.Event.START_ARRAY) { + return parser.getArray(); + } else if (e == JsonParser.Event.START_OBJECT) { + return parser.getObject(); + } + } catch (IllegalStateException ise) { + throw new JsonParsingException(ise.getMessage(), ise, parser.getLastCharLocation()); + } + } + throw new JsonException(JsonMessages.INTERNAL_ERROR()); + } + + @Override + public JsonObject readObject() { + if (readDone) { + throw new IllegalStateException(JsonMessages.READER_READ_ALREADY_CALLED()); + } + readDone = true; + if (parser.hasNext()) { + try { + parser.next(); + return parser.getObject(); + } catch (IllegalStateException ise) { + throw new JsonParsingException(ise.getMessage(), ise, parser.getLastCharLocation()); + } + } + throw new JsonException(JsonMessages.INTERNAL_ERROR()); + } + + @Override + public JsonArray readArray() { + if (readDone) { + throw new IllegalStateException(JsonMessages.READER_READ_ALREADY_CALLED()); + } + readDone = true; + if (parser.hasNext()) { + try { + parser.next(); + return parser.getArray(); + } catch (IllegalStateException ise) { + throw new JsonParsingException(ise.getMessage(), ise, parser.getLastCharLocation()); + } + } + throw new JsonException(JsonMessages.INTERNAL_ERROR()); + } + + @Override + public JsonValue readValue() { + if (readDone) { + throw new IllegalStateException(JsonMessages.READER_READ_ALREADY_CALLED()); + } + readDone = true; + if (parser.hasNext()) { + try { + parser.next(); + return parser.getValue(); + } catch (IllegalStateException ise) { + throw new JsonParsingException(ise.getMessage(), ise, parser.getLastCharLocation()); + } + } + throw new JsonException(JsonMessages.INTERNAL_ERROR()); + } + + @Override + public void close() { + readDone = true; + parser.close(); + } +} diff --git a/impl/src/main/java/org/glassfish/json/JsonStringImpl.java b/impl/src/main/java/org/glassfish/json/JsonStringImpl.java new file mode 100644 index 00000000..5963fd4c --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonStringImpl.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import javax.json.JsonString; + +/** + * JsonString impl + * + * @author Jitendra Kotamraju + */ +final class JsonStringImpl implements JsonString { + + private final String value; + + JsonStringImpl(String value) { + this.value = value; + } + + @Override + public String getString() { + return value; + } + + @Override + public CharSequence getChars() { + return value; + } + + @Override + public ValueType getValueType() { + return ValueType.STRING; + } + + @Override + public int hashCode() { + return getString().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj){ + return true; + } + if (!(obj instanceof JsonString)) { + return false; + } + JsonString other = (JsonString)obj; + return getString().equals(other.getString()); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append('"'); + + for(int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + // unescaped = %x20-21 | %x23-5B | %x5D-10FFFF + if (c >= 0x20 && c <= 0x10ffff && c != 0x22 && c != 0x5c) { + sb.append(c); + } else { + switch (c) { + case '"': + case '\\': + sb.append('\\'); sb.append(c); + break; + case '\b': + sb.append('\\'); sb.append('b'); + break; + case '\f': + sb.append('\\'); sb.append('f'); + break; + case '\n': + sb.append('\\'); sb.append('n'); + break; + case '\r': + sb.append('\\'); sb.append('r'); + break; + case '\t': + sb.append('\\'); sb.append('t'); + break; + default: + String hex = "000" + Integer.toHexString(c); + sb.append("\\u").append(hex.substring(hex.length() - 4)); + } + } + } + + sb.append('"'); + return sb.toString(); + } +} + diff --git a/impl/src/main/java/org/glassfish/json/JsonStructureParser.java b/impl/src/main/java/org/glassfish/json/JsonStructureParser.java new file mode 100644 index 00000000..b3ae8e66 --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonStructureParser.java @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import javax.json.*; +import javax.json.stream.JsonLocation; +import javax.json.stream.JsonParser; +import java.math.BigDecimal; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; + +/** + * {@link JsonParser} implementation on top of JsonArray/JsonObject + * + * @author Jitendra Kotamraju + */ +class JsonStructureParser implements JsonParser { + + private Scope current; + private Event state; + private final Deque scopeStack = new ArrayDeque<>(); + + JsonStructureParser(JsonArray array) { + current = new ArrayScope(array); + } + + JsonStructureParser(JsonObject object) { + current = new ObjectScope(object); + } + + @Override + public String getString() { + switch (state) { + case KEY_NAME: + return ((ObjectScope)current).key; + case VALUE_STRING: + return ((JsonString)current.getJsonValue()).getString(); + case VALUE_NUMBER: + return ((JsonNumber)current.getJsonValue()).toString(); + default: + throw new IllegalStateException(JsonMessages.PARSER_GETSTRING_ERR(state)); + } + } + + @Override + public boolean isIntegralNumber() { + if (state == Event.VALUE_NUMBER) { + return ((JsonNumber)current.getJsonValue()).isIntegral(); + } + throw new IllegalStateException(JsonMessages.PARSER_ISINTEGRALNUMBER_ERR(state)); + } + + @Override + public int getInt() { + if (state == Event.VALUE_NUMBER) { + return ((JsonNumber)current.getJsonValue()).intValue(); + } + throw new IllegalStateException(JsonMessages.PARSER_GETINT_ERR(state)); + } + + @Override + public long getLong() { + if (state == Event.VALUE_NUMBER) { + return ((JsonNumber)current.getJsonValue()).longValue(); + } + throw new IllegalStateException(JsonMessages.PARSER_GETLONG_ERR(state)); + } + + @Override + public BigDecimal getBigDecimal() { + if (state == Event.VALUE_NUMBER) { + return ((JsonNumber)current.getJsonValue()).bigDecimalValue(); + } + throw new IllegalStateException(JsonMessages.PARSER_GETBIGDECIMAL_ERR(state)); + } + + @Override + public JsonLocation getLocation() { + return JsonLocationImpl.UNKNOWN; + } + + @Override + public boolean hasNext() { + return !((state == Event.END_OBJECT || state == Event.END_ARRAY) && scopeStack.isEmpty()); + } + + @Override + public Event next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + transition(); + return state; + } + + private void transition() { + if (state == null) { + state = current instanceof ArrayScope ? Event.START_ARRAY : Event.START_OBJECT; + } else { + if (state == Event.END_OBJECT || state == Event.END_ARRAY) { + current = scopeStack.pop(); + } + if (current instanceof ArrayScope) { + if (current.hasNext()) { + current.next(); + state = getState(current.getJsonValue()); + if (state == Event.START_ARRAY || state == Event.START_OBJECT) { + scopeStack.push(current); + current = Scope.createScope(current.getJsonValue()); + } + } else { + state = Event.END_ARRAY; + } + } else { + // ObjectScope + if (state == Event.KEY_NAME) { + state = getState(current.getJsonValue()); + if (state == Event.START_ARRAY || state == Event.START_OBJECT) { + scopeStack.push(current); + current = Scope.createScope(current.getJsonValue()); + } + } else { + if (current.hasNext()) { + current.next(); + state = Event.KEY_NAME; + } else { + state = Event.END_OBJECT; + } + } + } + } + } + + @Override + public void close() { + // no-op + } + + @Override + public void skipObject() { + if (current instanceof ObjectScope) { + int depth = 1; + do { + if (state == Event.KEY_NAME) { + state = getState(current.getJsonValue()); + switch (state) { + case START_OBJECT: + depth++; + break; + case END_OBJECT: + depth--; + break; + default: + //no-op + } + } else { + if (current.hasNext()) { + current.next(); + state = Event.KEY_NAME; + } else { + state = Event.END_OBJECT; + depth--; + } + } + } while (state != Event.END_OBJECT && depth > 0); + } + } + + @Override + public void skipArray() { + if (current instanceof ArrayScope) { + int depth = 1; + do { + if (current.hasNext()) { + current.next(); + state = getState(current.getJsonValue()); + switch (state) { + case START_ARRAY: + depth++; + break; + case END_ARRAY: + depth--; + break; + default: + //no-op + } + } else { + state = Event.END_ARRAY; + depth--; + } + } while (!(state == Event.END_ARRAY && depth == 0)); + } + } + + private static Event getState(JsonValue value) { + switch (value.getValueType()) { + case ARRAY: + return Event.START_ARRAY; + case OBJECT: + return Event.START_OBJECT; + case STRING: + return Event.VALUE_STRING; + case NUMBER: + return Event.VALUE_NUMBER; + case TRUE: + return Event.VALUE_TRUE; + case FALSE: + return Event.VALUE_FALSE; + case NULL: + return Event.VALUE_NULL; + default: + throw new JsonException(JsonMessages.PARSER_STATE_ERR(value.getValueType())); + } + } + + private static abstract class Scope implements Iterator { + abstract JsonValue getJsonValue(); + + static Scope createScope(JsonValue value) { + if (value instanceof JsonArray) { + return new ArrayScope((JsonArray)value); + } else if (value instanceof JsonObject) { + return new ObjectScope((JsonObject)value); + } + throw new JsonException(JsonMessages.PARSER_SCOPE_ERR(value)); + } + } + + private static class ArrayScope extends Scope { + private final Iterator it; + private JsonValue value; + + ArrayScope(JsonArray array) { + this.it = array.iterator(); + } + + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public JsonValue next() { + value = it.next(); + return value; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + @Override + JsonValue getJsonValue() { + return value; + } + + } + + private static class ObjectScope extends Scope { + private final Iterator> it; + private JsonValue value; + private String key; + + ObjectScope(JsonObject object) { + this.it = object.entrySet().iterator(); + } + + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public Map.Entry next() { + Map.Entry next = it.next(); + this.key = next.getKey(); + this.value = next.getValue(); + return next; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + @Override + JsonValue getJsonValue() { + return value; + } + + } + +} diff --git a/impl/src/main/java/org/glassfish/json/JsonTokenizer.java b/impl/src/main/java/org/glassfish/json/JsonTokenizer.java new file mode 100644 index 00000000..f9ea4335 --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonTokenizer.java @@ -0,0 +1,586 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import org.glassfish.json.api.BufferPool; + +import javax.json.JsonException; +import javax.json.stream.JsonLocation; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParsingException; +import java.io.*; +import java.math.BigDecimal; +import java.util.Arrays; + +import javax.json.stream.JsonParser.Event; + +/** + * JSON Tokenizer + * + * @author Jitendra Kotamraju + */ +final class JsonTokenizer implements Closeable { + // Table to look up hex ch -> value (for e.g HEX['F'] = 15, HEX['5'] = 5) + private final static int[] HEX = new int[128]; + static { + Arrays.fill(HEX, -1); + for (int i='0'; i <= '9'; i++) { + HEX[i] = i-'0'; + } + for (int i='A'; i <= 'F'; i++) { + HEX[i] = 10+i-'A'; + } + for (int i='a'; i <= 'f'; i++) { + HEX[i] = 10+i-'a'; + } + } + private final static int HEX_LENGTH = HEX.length; + + private final BufferPool bufferPool; + + private final Reader reader; + + // Internal buffer that is used for parsing. It is also used + // for storing current string and number value token + private char[] buf; + + // Indexes in buffer + // + // XXXssssssssssssXXXXXXXXXXXXXXXXXXXXXXrrrrrrrrrrrrrrXXXXXX + // ^ ^ ^ ^ + // | | | | + // storeBegin storeEnd readBegin readEnd + private int readBegin; + private int readEnd; + private int storeBegin; + private int storeEnd; + + // line number of the current pointer of parsing char + private long lineNo = 1; + + // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + // ^ + // | + // bufferOffset + // + // offset of the last \r\n or \n. will be used to calculate column number + // of a token or an error. This may be outside of the buffer. + private long lastLineOffset = 0; + // offset in the stream for the start of the buffer, will be used in + // calculating JsonLocation's stream offset, column no. + private long bufferOffset = 0; + + private boolean minus; + private boolean fracOrExp; + private BigDecimal bd; + + enum JsonToken { + CURLYOPEN(Event.START_OBJECT, false), + SQUAREOPEN(Event.START_ARRAY, false), + COLON(null, false), + COMMA(null, false), + STRING(Event.VALUE_STRING, true), + NUMBER(Event.VALUE_NUMBER, true), + TRUE(Event.VALUE_TRUE, true), + FALSE(Event.VALUE_FALSE, true), + NULL(Event.VALUE_NULL, true), + CURLYCLOSE(Event.END_OBJECT, false), + SQUARECLOSE(Event.END_ARRAY, false), + EOF(null, false); + + private final JsonParser.Event event; + private final boolean value; + + JsonToken(JsonParser.Event event, boolean value) { + this.event = event; + this.value = value; + } + + JsonParser.Event getEvent() { + return event; + } + + boolean isValue() { + return value; + } + } + + JsonTokenizer(Reader reader, BufferPool bufferPool) { + this.reader = reader; + this.bufferPool = bufferPool; + buf = bufferPool.take(); + } + + private void readString() { + // when inPlace is true, no need to copy chars + boolean inPlace = true; + storeBegin = storeEnd = readBegin; + + do { + // Write unescaped char block within the current buffer + if (inPlace) { + int ch; + while(readBegin < readEnd && ((ch=buf[readBegin]) >= 0x20) && ch != '\\') { + if (ch == '"') { + storeEnd = readBegin++; // ++ to consume quote char + return; // Got the entire string + } + readBegin++; // consume unescaped char + } + storeEnd = readBegin; + } + + // string may be crossing buffer boundaries and may contain + // escaped characters. + int ch = read(); + if (ch >= 0x20 && ch != 0x22 && ch != 0x5c) { + if (!inPlace) { + buf[storeEnd] = (char)ch; + } + storeEnd++; + continue; + } + switch (ch) { + case '\\': + inPlace = false; // Now onwards need to copy chars + unescape(); + break; + case '"': + return; + default: + throw unexpectedChar(ch); + } + } while (true); + } + + private void unescape() { + int ch = read(); + switch (ch) { + case 'b': + buf[storeEnd++] = '\b'; + break; + case 't': + buf[storeEnd++] = '\t'; + break; + case 'n': + buf[storeEnd++] = '\n'; + break; + case 'f': + buf[storeEnd++] = '\f'; + break; + case 'r': + buf[storeEnd++] = '\r'; + break; + case '"': + case '\\': + case '/': + buf[storeEnd++] = (char)ch; + break; + case 'u': { + int unicode = 0; + for (int i = 0; i < 4; i++) { + int ch3 = read(); + int digit = (ch3 >= 0 && ch3 < HEX_LENGTH) ? HEX[ch3] : -1; + if (digit < 0) { + throw unexpectedChar(ch3); + } + unicode = (unicode << 4)|digit; + } + buf[storeEnd++] = (char)unicode; + break; + } + default: + throw unexpectedChar(ch); + } + } + + // Reads a number char. If the char is within the buffer, directly + // reads from the buffer. Otherwise, uses read() which takes care + // of resizing, filling up the buf, adjusting the pointers + private int readNumberChar() { + if (readBegin < readEnd) { + return buf[readBegin++]; + } else { + storeEnd = readBegin; + return read(); + } + } + + private void readNumber(int ch) { + storeBegin = storeEnd = readBegin-1; + // sign + if (ch == '-') { + this.minus = true; + ch = readNumberChar(); + if (ch < '0' || ch >'9') { + throw unexpectedChar(ch); + } + } + + // int + if (ch == '0') { + ch = readNumberChar(); + } else { + do { + ch = readNumberChar(); + } while (ch >= '0' && ch <= '9'); + } + + // frac + if (ch == '.') { + this.fracOrExp = true; + int count = 0; + do { + ch = readNumberChar(); + count++; + } while (ch >= '0' && ch <= '9'); + if (count == 1) { + throw unexpectedChar(ch); + } + } + + // exp + if (ch == 'e' || ch == 'E') { + this.fracOrExp = true; + ch = readNumberChar(); + if (ch == '+' || ch == '-') { + ch = readNumberChar(); + } + int count; + for (count = 0; ch >= '0' && ch <= '9'; count++) { + ch = readNumberChar(); + } + if (count == 0) { + throw unexpectedChar(ch); + } + } + if (ch != -1) { + // Only reset readBegin if eof has not been reached + readBegin--; + storeEnd = readBegin; + } + } + + private void readTrue() { + int ch1 = read(); + if (ch1 != 'r') { + throw expectedChar(ch1, 'r'); + } + int ch2 = read(); + if (ch2 != 'u') { + throw expectedChar(ch2, 'u'); + } + int ch3 = read(); + if (ch3 != 'e') { + throw expectedChar(ch3, 'e'); + } + } + + private void readFalse() { + int ch1 = read(); + if (ch1 != 'a') { + throw expectedChar(ch1, 'a'); + } + int ch2 = read(); + if (ch2 != 'l') { + throw expectedChar(ch2, 'l'); + } + int ch3 = read(); + if (ch3 != 's') { + throw expectedChar(ch3, 's'); + } + int ch4 = read(); + if (ch4 != 'e') { + throw expectedChar(ch4, 'e'); + } + } + + private void readNull() { + int ch1 = read(); + if (ch1 != 'u') { + throw expectedChar(ch1, 'u'); + } + int ch2 = read(); + if (ch2 != 'l') { + throw expectedChar(ch2, 'l'); + } + int ch3 = read(); + if (ch3 != 'l') { + throw expectedChar(ch3, 'l'); + } + } + + /* + * Could be optimized if the parser uses separate methods to match colon + * etc (that would avoid the switch statement cost in certain cases) + */ + JsonToken nextToken() { + reset(); + int ch = read(); + + // whitespace + while (ch == 0x20 || ch == 0x09 || ch == 0x0a || ch == 0x0d) { + if (ch == '\r') { + ++lineNo; + ch = read(); + if (ch == '\n') { + lastLineOffset = bufferOffset+readBegin; + } else { + lastLineOffset = bufferOffset+readBegin-1; + continue; + } + } else if (ch == '\n') { + ++lineNo; + lastLineOffset = bufferOffset+readBegin; + } + ch = read(); + } + + switch (ch) { + case '"': + readString(); + return JsonToken.STRING; + case '{': + return JsonToken.CURLYOPEN; + case '[': + return JsonToken.SQUAREOPEN; + case ':': + return JsonToken.COLON; + case ',': + return JsonToken.COMMA; + case 't': + readTrue(); + return JsonToken.TRUE; + case 'f': + readFalse(); + return JsonToken.FALSE; + case 'n': + readNull(); + return JsonToken.NULL; + case ']': + return JsonToken.SQUARECLOSE; + case '}': + return JsonToken.CURLYCLOSE; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + readNumber(ch); + return JsonToken.NUMBER; + case -1: + return JsonToken.EOF; + default: + throw unexpectedChar(ch); + } + } + + boolean hasNextToken() { + reset(); + int ch = peek(); + + // whitespace + while (ch == 0x20 || ch == 0x09 || ch == 0x0a || ch == 0x0d) { + if (ch == '\r') { + ++lineNo; + ++readBegin; + ch = peek(); + if (ch == '\n') { + lastLineOffset = bufferOffset+readBegin+1; + } else { + lastLineOffset = bufferOffset+readBegin; + continue; + } + } else if (ch == '\n') { + ++lineNo; + lastLineOffset = bufferOffset+readBegin+1; + } + ++readBegin; + ch = peek(); + } + return ch != -1; + } + + private int peek() { + try { + if (readBegin == readEnd) { // need to fill the buffer + int len = fillBuf(); + if (len == -1) { + return -1; + } + assert len != 0; + readBegin = storeEnd; + readEnd = readBegin+len; + } + return buf[readBegin]; + } catch (IOException ioe) { + throw new JsonException(JsonMessages.TOKENIZER_IO_ERR(), ioe); + } + } + + // Gives the location of the last char. Used for + // JsonParsingException.getLocation + JsonLocation getLastCharLocation() { + // Already read the char, so subtracting -1 + return new JsonLocationImpl(lineNo, bufferOffset +readBegin-lastLineOffset, bufferOffset +readBegin-1); + } + + // Gives the parser location. Used for JsonParser.getLocation + JsonLocation getLocation() { + return new JsonLocationImpl(lineNo, bufferOffset +readBegin-lastLineOffset+1, bufferOffset +readBegin); + } + + private int read() { + try { + if (readBegin == readEnd) { // need to fill the buffer + int len = fillBuf(); + if (len == -1) { + return -1; + } + assert len != 0; + readBegin = storeEnd; + readEnd = readBegin+len; + } + return buf[readBegin++]; + } catch (IOException ioe) { + throw new JsonException(JsonMessages.TOKENIZER_IO_ERR(), ioe); + } + } + + private int fillBuf() throws IOException { + if (storeEnd != 0) { + int storeLen = storeEnd-storeBegin; + if (storeLen > 0) { + // there is some store data + if (storeLen == buf.length) { + // buffer is full, double the capacity + char[] doubleBuf = Arrays.copyOf(buf, 2 * buf.length); + bufferPool.recycle(buf); + buf = doubleBuf; + } else { + // Left shift all the stored data to make space + System.arraycopy(buf, storeBegin, buf, 0, storeLen); + storeEnd = storeLen; + storeBegin = 0; + bufferOffset += readBegin-storeEnd; + } + } else { + storeBegin = storeEnd = 0; + bufferOffset += readBegin; + } + } else { + bufferOffset += readBegin; + } + // Fill the rest of the buf + return reader.read(buf, storeEnd, buf.length-storeEnd); + } + + // state associated with the current token is no more valid + private void reset() { + if (storeEnd != 0) { + storeBegin = 0; + storeEnd = 0; + bd = null; + minus = false; + fracOrExp = false; + } + } + + String getValue() { + return new String(buf, storeBegin, storeEnd-storeBegin); + } + + BigDecimal getBigDecimal() { + if (bd == null) { + bd = new BigDecimal(buf, storeBegin, storeEnd-storeBegin); + } + return bd; + } + + int getInt() { + // no need to create BigDecimal for common integer values (1-9 digits) + int storeLen = storeEnd-storeBegin; + if (!fracOrExp && (storeLen <= 9 || (minus && storeLen <= 10))) { + int num = 0; + int i = minus ? 1 : 0; + for(; i < storeLen; i++) { + num = num * 10 + (buf[storeBegin+i] - '0'); + } + return minus ? -num : num; + } else { + return getBigDecimal().intValue(); + } + } + + long getLong() { + // no need to create BigDecimal for common integer values (1-18 digits) + int storeLen = storeEnd-storeBegin; + if (!fracOrExp && (storeLen <= 18 || (minus && storeLen <= 19))) { + long num = 0; + int i = minus ? 1 : 0; + for(; i < storeLen; i++) { + num = num * 10 + (buf[storeBegin+i] - '0'); + } + return minus ? -num : num; + } else { + return getBigDecimal().longValue(); + } + } + + // returns true for common integer values (1-9 digits). + // So there are cases it will return false even though the number is int + boolean isDefinitelyInt() { + int storeLen = storeEnd-storeBegin; + return !fracOrExp && (storeLen <= 9 || (minus && storeLen <= 10)); + } + + // returns true for common long values (1-18 digits). + // So there are cases it will return false even though the number is long + boolean isDefinitelyLong() { + int storeLen = storeEnd-storeBegin; + return !fracOrExp && (storeLen <= 18 || (minus && storeLen <= 19)); + } + + boolean isIntegral() { + return !fracOrExp || getBigDecimal().scale() == 0; + } + + @Override + public void close() throws IOException { + reader.close(); + bufferPool.recycle(buf); + } + + private JsonParsingException unexpectedChar(int ch) { + JsonLocation location = getLastCharLocation(); + return new JsonParsingException( + JsonMessages.TOKENIZER_UNEXPECTED_CHAR(ch, location), location); + } + + private JsonParsingException expectedChar(int unexpected, char expected) { + JsonLocation location = getLastCharLocation(); + return new JsonParsingException( + JsonMessages.TOKENIZER_EXPECTED_CHAR(unexpected, location, expected), location); + } + +} diff --git a/impl/src/main/java/org/glassfish/json/JsonUtil.java b/impl/src/main/java/org/glassfish/json/JsonUtil.java new file mode 100644 index 00000000..e077f12d --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonUtil.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import java.io.StringReader; +import javax.json.Json; +import javax.json.JsonReader; +import javax.json.JsonValue; +import javax.json.stream.JsonParsingException; + +/** + * A utility class + * + * @since 1.1 + */ +public final class JsonUtil { + + private JsonUtil() { + } + + /** + * Reads the input JSON text and returns a JsonValue. + *

For convenience, single quotes as well as double quotes + * are allowed to delimit JSON strings. If single quotes are + * used, any quotes, single or double, in the JSON string must be + * escaped (prepend with a '\'). + * + * @param jsonString the input JSON data + * @return the object model for {@code jsonString} + * @throws JsonParsingException if the input is not legal JSON text + */ + public static JsonValue toJson(String jsonString) { + StringBuilder builder = new StringBuilder(); + boolean single_context = false; + for (int i = 0; i < jsonString.length(); i++) { + char ch = jsonString.charAt(i); + if (ch == '\\') { + i = i + 1; + if (i < jsonString.length()) { + ch = jsonString.charAt(i); + if (!(single_context && ch == '\'')) { + // unescape ' inside single quotes + builder.append('\\'); + } + } + } else if (ch == '\'') { + // Turn ' into ", for proper JSON string + ch = '"'; + single_context = ! single_context; + } + builder.append(ch); + } + + JsonReader reader = Json.createReader( + new StringReader(builder.toString())); + JsonValue value = reader.readValue(); + reader.close(); + return value; + } +} + diff --git a/impl/src/main/java/org/glassfish/json/JsonWriterFactoryImpl.java b/impl/src/main/java/org/glassfish/json/JsonWriterFactoryImpl.java new file mode 100644 index 00000000..c59afc9c --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonWriterFactoryImpl.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import org.glassfish.json.api.BufferPool; + +import javax.json.JsonWriter; +import javax.json.JsonWriterFactory; +import java.io.OutputStream; +import java.io.Writer; +import java.nio.charset.Charset; +import java.util.Map; + +/** + * @author Jitendra Kotamraju + */ +class JsonWriterFactoryImpl implements JsonWriterFactory { + private final Map config; // unmodifiable map + private final boolean prettyPrinting; + private final BufferPool bufferPool; + + JsonWriterFactoryImpl(Map config, boolean prettyPrinting, + BufferPool bufferPool) { + this.config = config; + this.prettyPrinting = prettyPrinting; + this.bufferPool = bufferPool; + } + + @Override + public JsonWriter createWriter(Writer writer) { + return new JsonWriterImpl(writer, prettyPrinting, bufferPool); + } + + @Override + public JsonWriter createWriter(OutputStream out) { + return new JsonWriterImpl(out, prettyPrinting, bufferPool); + } + + @Override + public JsonWriter createWriter(OutputStream out, Charset charset) { + return new JsonWriterImpl(out, charset, prettyPrinting, bufferPool); + } + + @Override + public Map getConfigInUse() { + return config; + } +} diff --git a/impl/src/main/java/org/glassfish/json/JsonWriterImpl.java b/impl/src/main/java/org/glassfish/json/JsonWriterImpl.java new file mode 100644 index 00000000..e29dccb4 --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/JsonWriterImpl.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import org.glassfish.json.api.BufferPool; + +import javax.json.*; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.Writer; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Map; + +/** + * JsonWriter impl using generator. + * + * @author Jitendra Kotamraju + */ +class JsonWriterImpl implements JsonWriter { + + private final JsonGeneratorImpl generator; + private boolean writeDone; + private final NoFlushOutputStream os; + + JsonWriterImpl(Writer writer, BufferPool bufferPool) { + this(writer, false, bufferPool); + } + + JsonWriterImpl(Writer writer, boolean prettyPrinting, BufferPool bufferPool) { + generator = prettyPrinting + ? new JsonPrettyGeneratorImpl(writer, bufferPool) + : new JsonGeneratorImpl(writer, bufferPool); + os = null; + } + + JsonWriterImpl(OutputStream out, BufferPool bufferPool) { + this(out, StandardCharsets.UTF_8, false, bufferPool); + } + + JsonWriterImpl(OutputStream out, boolean prettyPrinting, BufferPool bufferPool) { + this(out, StandardCharsets.UTF_8, prettyPrinting, bufferPool); + } + + JsonWriterImpl(OutputStream out, Charset charset, + boolean prettyPrinting, BufferPool bufferPool) { + // Decorating the given stream, so that buffered contents can be + // written without actually flushing the stream. + this.os = new NoFlushOutputStream(out); + generator = prettyPrinting + ? new JsonPrettyGeneratorImpl(os, charset, bufferPool) + : new JsonGeneratorImpl(os, charset, bufferPool); + } + + @Override + public void writeArray(JsonArray array) { + if (writeDone) { + throw new IllegalStateException(JsonMessages.WRITER_WRITE_ALREADY_CALLED()); + } + writeDone = true; + generator.writeStartArray(); + for(JsonValue value : array) { + generator.write(value); + } + generator.writeEnd(); + // Flush the generator's buffered contents. This won't work for byte + // streams as intermediary OutputStreamWriter buffers. + generator.flushBuffer(); + // Flush buffered contents but not the byte stream. generator.flush() + // does OutputStreamWriter#flushBuffer (package private) and underlying + // byte stream#flush(). Here underlying stream's flush() is no-op. + if (os != null) { + generator.flush(); + } + } + + @Override + public void writeObject(JsonObject object) { + if (writeDone) { + throw new IllegalStateException(JsonMessages.WRITER_WRITE_ALREADY_CALLED()); + } + writeDone = true; + generator.writeStartObject(); + for(Map.Entry e : object.entrySet()) { + generator.write(e.getKey(), e.getValue()); + } + generator.writeEnd(); + // Flush the generator's buffered contents. This won't work for byte + // streams as intermediary OutputStreamWriter buffers. + generator.flushBuffer(); + // Flush buffered contents but not the byte stream. generator.flush() + // does OutputStreamWriter#flushBuffer (package private) and underlying + // byte stream#flush(). Here underlying stream's flush() is no-op. + if (os != null) { + generator.flush(); + } + } + + @Override + public void write(JsonStructure value) { + if (value instanceof JsonArray) { + writeArray((JsonArray)value); + } else { + writeObject((JsonObject)value); + } + } + + @Override + public void write(JsonValue value) { + switch (value.getValueType()) { + case OBJECT: + writeObject((JsonObject) value); + return; + case ARRAY: + writeArray((JsonArray) value); + return; + default: + if (writeDone) { + throw new IllegalStateException(JsonMessages.WRITER_WRITE_ALREADY_CALLED()); + } + writeDone = true; + generator.write(value); + generator.flushBuffer(); + if (os != null) { + generator.flush(); + } + } + } + + @Override + public void close() { + writeDone = true; + generator.close(); + } + + private static final class NoFlushOutputStream extends FilterOutputStream { + public NoFlushOutputStream(OutputStream out) { + super(out); + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + out.write(b, off ,len); + } + + @Override + public void flush() { + // no-op + } + } + +} diff --git a/impl/src/main/java/org/glassfish/json/MapUtil.java b/impl/src/main/java/org/glassfish/json/MapUtil.java new file mode 100644 index 00000000..a8b88547 --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/MapUtil.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import org.glassfish.json.api.BufferPool; + +import javax.json.JsonArrayBuilder; +import javax.json.JsonObjectBuilder; +import javax.json.JsonValue; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Collection; +import java.util.Map; + +/** + * Util for transforming a Map to a Json objects. + * + * @author asotobu + */ +public final class MapUtil { + + private MapUtil() { + super(); + } + + static JsonValue handle(Object value, BufferPool bufferPool) { + + if (value == null) { + return JsonValue.NULL; + } + + if (value instanceof BigDecimal) { + return JsonNumberImpl.getJsonNumber((BigDecimal) value); + } else { + if (value instanceof BigInteger) { + return JsonNumberImpl.getJsonNumber((BigInteger) value); + } else { + if ( value instanceof Boolean) { + Boolean b = (Boolean) value; + return b ? JsonValue.TRUE : JsonValue.FALSE; + } else { + if (value instanceof Double) { + return JsonNumberImpl.getJsonNumber((Double) value); + } else { + if (value instanceof Integer) { + return JsonNumberImpl.getJsonNumber((Integer) value); + } else { + if (value instanceof Long) { + return JsonNumberImpl.getJsonNumber((Long) value); + } else { + if (value instanceof String) { + return new JsonStringImpl((String) value); + } else { + if (value instanceof Collection) { + @SuppressWarnings("unchecked") + Collection collection = (Collection) value; + JsonArrayBuilder jsonArrayBuilder = new JsonArrayBuilderImpl(collection, bufferPool); + return jsonArrayBuilder.build(); + } else { + if (value instanceof Map) { + @SuppressWarnings("unchecked") + JsonObjectBuilder object = new JsonObjectBuilderImpl((Map) value, bufferPool); + return object.build(); + } + } + } + } + } + } + } + } + } + + throw new IllegalArgumentException(String.format("Type %s is not supported.", value.getClass())); + } + +} diff --git a/impl/src/main/java/org/glassfish/json/NodeReference.java b/impl/src/main/java/org/glassfish/json/NodeReference.java new file mode 100644 index 00000000..4bf126c9 --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/NodeReference.java @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonArrayBuilder; +import javax.json.JsonException; +import javax.json.JsonObject; +import javax.json.JsonStructure; +import javax.json.JsonValue; + +/** + * This class is a helper class for JsonPointer implementation, + * and is not part of the API. + * + * This class encapsulates a reference to a JSON node. + * There are three types of references. + *

  1. a reference to the root of a JSON tree.
  2. + *
  3. a reference to a name/value (possibly non-existing) pair of a JSON object, identified by a name.
  4. + *
  5. a reference to a member value of a JSON array, identified by an index.
  6. + *
+ * Static factory methods are provided for creating these references. + * + *

A referenced value can be retrieved or replaced. + * The value of a JSON object or JSON array can be + * removed. A new value can be added to a JSON object or + * inserted into a JSON array

+ * + *

Since a {@code JsonObject} or {@code JsonArray} is immutable, these operations + * must not modify the referenced JSON object or array. The methods {@link #add}, + * {@link #replace}, and {@link #remove} returns a new + * JSON object or array after the execution of the operation.

+ */ +abstract class NodeReference { + + /** + * Return {@code true} if a reference points to a valid value, {@code false} otherwise. + * + * @return {@code true} if a reference points to a value + */ + abstract public boolean contains(); + + /** + * Get the value at the referenced location. + * + * @return the JSON value referenced + * @throws JsonException if the referenced value does not exist + */ + abstract public JsonValue get(); + + /** + * Add or replace a value at the referenced location. + * If the reference is the root of a JSON tree, the added value must be + * a JSON object or array, which becomes the referenced JSON value. + * If the reference is an index of a JSON array, the value is inserted + * into the array at the index. If the index is -1, the value is + * appended to the array. + * If the reference is a name of a JSON object, the name/value pair is added + * to the object, replacing any pair with the same name. + * + * @param value the value to be added + * @return the JsonStructure after the operation + * @throws JsonException if the index to the array is not -1 or is out of range + */ + abstract public JsonStructure add(JsonValue value); + + /** + * Remove the name/value pair from the JSON object, or the value in a JSON array, as specified by the reference + * + * @return the JsonStructure after the operation + * @throws JsonException if the name/value pair of the referenced JSON object + * does not exist, or if the index of the referenced JSON array is + * out of range, or if the reference is a root reference + */ + abstract public JsonStructure remove(); + + /** + * Replace the referenced value with the specified value. + * + * @param value the JSON value to be stored at the referenced location + * @return the JsonStructure after the operation + * @throws JsonException if the name/value pair of the referenced JSON object + * does not exist, or if the index of the referenced JSON array is + * out of range, or if the reference is a root reference + */ + abstract public JsonStructure replace(JsonValue value); + + /** + * Returns a {@code NodeReference} for a {@code JsonStructure}. + * + * @param structure the {@code JsonStructure} referenced + * @return the {@code NodeReference} + */ + public static NodeReference of(JsonStructure structure) { + return new RootReference(structure); + } + + /** + * Returns a {@code NodeReference} for a name/value pair in a + * JSON object. + * + * @param object the referenced JSON object + * @param name the name of the name/pair + * @return the {@code NodeReference} + */ + public static NodeReference of(JsonObject object, String name) { + return new ObjectReference(object, name); + } + + /** + * Returns a {@code NodeReference} for a member value in a + * JSON array. + * + * @param array the referenced JSON array + * @param index the index of the member value in the JSON array + * @return the {@code NodeReference} + */ + public static NodeReference of(JsonArray array, int index) { + return new ArrayReference(array, index); + } + + static class RootReference extends NodeReference { + + private JsonStructure root; + + RootReference(JsonStructure root) { + this.root = root; + } + + @Override + public boolean contains() { + return root != null; + } + + @Override + public JsonValue get() { + return root; + } + + @Override + public JsonStructure add(JsonValue value) { + switch (value.getValueType() ) { + case OBJECT: + case ARRAY: + this.root = (JsonStructure) value; + break; + default: + throw new JsonException(JsonMessages.NODEREF_VALUE_ADD_ERR()); + } + return root; + } + + @Override + public JsonStructure remove() { + throw new JsonException(JsonMessages.NODEREF_VALUE_CANNOT_REMOVE()); + } + + @Override + public JsonStructure replace(JsonValue value) { + return add(value); + } + } + + static class ObjectReference extends NodeReference { + + private final JsonObject object; + private final String key; + + ObjectReference(JsonObject object, String key) { + this.object = object; + this.key = key; + } + + @Override + public boolean contains() { + return object != null && object.containsKey(key); + } + + @Override + public JsonValue get() { + if (!contains()) { + throw new JsonException(JsonMessages.NODEREF_OBJECT_MISSING(key)); + } + return object.get(key); + } + + @Override + public JsonObject add(JsonValue value) { + return Json.createObjectBuilder(object).add(key, value).build(); + } + + @Override + public JsonObject remove() { + if (!contains()) { + throw new JsonException(JsonMessages.NODEREF_OBJECT_MISSING(key)); + } + return Json.createObjectBuilder(object).remove(key).build(); + } + + @Override + public JsonObject replace(JsonValue value) { + if (!contains()) { + throw new JsonException(JsonMessages.NODEREF_OBJECT_MISSING(key)); + } + return add(value); + } + } + + static class ArrayReference extends NodeReference { + + private final JsonArray array; + private final int index; // -1 means "-" in JSON Pointer + + ArrayReference(JsonArray array, int index) { + this.array = array; + this.index = index; + } + + @Override + public boolean contains() { + return array != null && index > -1 && index < array.size(); + } + + @Override + public JsonValue get() { + if (!contains()) { + throw new JsonException(JsonMessages.NODEREF_ARRAY_INDEX_ERR(index, array.size())); + } + return array.get(index); + } + + @Override + public JsonArray add(JsonValue value) { + //TODO should we check for arrayoutofbounds? + // The spec seems to say index = array.size() is allowed. This is handled as append + JsonArrayBuilder builder = Json.createArrayBuilder(this.array); + if (index == -1 || index == array.size()) { + builder.add(value); + } else { + if(index < array.size()) { + builder.add(index, value); + } else { + throw new JsonException(JsonMessages.NODEREF_ARRAY_INDEX_ERR(index, array.size())); + } + } + return builder.build(); + } + + @Override + public JsonArray remove() { + if (!contains()) { + throw new JsonException(JsonMessages.NODEREF_ARRAY_INDEX_ERR(index, array.size())); + } + JsonArrayBuilder builder = Json.createArrayBuilder(this.array); + return builder.remove(index).build(); + } + + @Override + public JsonArray replace(JsonValue value) { + if (!contains()) { + throw new JsonException(JsonMessages.NODEREF_ARRAY_INDEX_ERR(index, array.size())); + } + JsonArrayBuilder builder = Json.createArrayBuilder(this.array); + return builder.set(index, value).build(); + } + } +} + diff --git a/impl/src/main/java/org/glassfish/json/UnicodeDetectingInputStream.java b/impl/src/main/java/org/glassfish/json/UnicodeDetectingInputStream.java new file mode 100644 index 00000000..57ec2627 --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/UnicodeDetectingInputStream.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json; + +import javax.json.JsonException; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +/** + * A filter stream that detects the unicode encoding for the original + * stream + * + * @author Jitendra Kotamraju + */ +class UnicodeDetectingInputStream extends FilterInputStream { + + private static final Charset UTF_32LE = Charset.forName("UTF-32LE"); + private static final Charset UTF_32BE = Charset.forName("UTF-32BE"); + + private static final byte FF = (byte)0xFF; + private static final byte FE = (byte)0xFE; + private static final byte EF = (byte)0xEF; + private static final byte BB = (byte)0xBB; + private static final byte BF = (byte)0xBF; + private static final byte NUL = (byte)0x00; + + private final byte[] buf = new byte[4]; + private int bufLen; + private int curIndex; + private final Charset charset; + + UnicodeDetectingInputStream(InputStream is) { + super(is); + charset = detectEncoding(); + } + + Charset getCharset() { + return charset; + } + + private void fillBuf() { + int b1; + int b2; + int b3; + int b4; + + try { + b1 = in.read(); + if (b1 == -1) { + return; + } + + b2 = in.read(); + if (b2 == -1) { + bufLen = 1; + buf[0] = (byte)b1; + return; + } + + b3 = in.read(); + if (b3 == -1) { + bufLen = 2; + buf[0] = (byte)b1; + buf[1] = (byte)b2; + return; + } + + b4 = in.read(); + if (b4 == -1) { + bufLen = 3; + buf[0] = (byte)b1; + buf[1] = (byte)b2; + buf[2] = (byte)b3; + return; + } + bufLen = 4; + buf[0] = (byte)b1; + buf[1] = (byte)b2; + buf[2] = (byte)b3; + buf[3] = (byte)b4; + } catch (IOException ioe) { + throw new JsonException(JsonMessages.PARSER_INPUT_ENC_DETECT_IOERR(), ioe); + } + } + + private Charset detectEncoding() { + fillBuf(); + if (bufLen < 2) { + throw new JsonException(JsonMessages.PARSER_INPUT_ENC_DETECT_FAILED()); + } else if (bufLen == 4) { + // Use BOM to detect encoding + if (buf[0] == NUL && buf[1] == NUL && buf[2] == FE && buf[3] == FF) { + curIndex = 4; + return UTF_32BE; + } else if (buf[0] == FF && buf[1] == FE && buf[2] == NUL && buf[3] == NUL) { + curIndex = 4; + return UTF_32LE; + } else if (buf[0] == FE && buf[1] == FF) { + curIndex = 2; + return StandardCharsets.UTF_16BE; + } else if (buf[0] == FF && buf[1] == FE) { + curIndex = 2; + return StandardCharsets.UTF_16LE; + } else if (buf[0] == EF && buf[1] == BB && buf[2] == BF) { + curIndex = 3; + return StandardCharsets.UTF_8; + } + // No BOM, just use JSON RFC's encoding algo to auto-detect + if (buf[0] == NUL && buf[1] == NUL && buf[2] == NUL) { + return UTF_32BE; + } else if (buf[0] == NUL && buf[2] == NUL) { + return StandardCharsets.UTF_16BE; + } else if (buf[1] == NUL && buf[2] == NUL && buf[3] == NUL) { + return UTF_32LE; + } else if (buf[1] == NUL && buf[3] == NUL) { + return StandardCharsets.UTF_16LE; + } + } + return StandardCharsets.UTF_8; + } + + @Override + public int read() throws IOException { + if (curIndex < bufLen) { + return buf[curIndex++]; + } + return in.read(); + } + + @Override + public int read(byte b[], int off, int len) throws IOException { + if (curIndex < bufLen) { + if (len == 0) { + return 0; + } + if (off < 0 || len < 0 || len > b.length -off) { + throw new IndexOutOfBoundsException(); + } + int min = Math.min(bufLen-curIndex, len); + System.arraycopy(buf, curIndex, b, off, min); + curIndex += min; + return min; + } + return in.read(b, off, len); + } + +} diff --git a/impl/src/main/java/org/glassfish/json/api/BufferPool.java b/impl/src/main/java/org/glassfish/json/api/BufferPool.java new file mode 100644 index 00000000..7253615f --- /dev/null +++ b/impl/src/main/java/org/glassfish/json/api/BufferPool.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.api; + +/** + * char[] pool that pool instances of char[] which are expensive to create. + * + * @author Jitendra Kotamraju + */ +public interface BufferPool { + + /** + * Gets a new char[] object from the pool. + * + *

+ * If no object is available in the pool, this method creates a new one. + * + * @return + * always non-null. + */ + char[] take(); + + /** + * Returns an object back to the pool. + * + * @param buf object to return back to the pool + */ + void recycle(char[] buf); + +} diff --git a/impl/src/main/jdk9/module-info.java b/impl/src/main/jdk9/module-info.java new file mode 100644 index 00000000..9e451a9a --- /dev/null +++ b/impl/src/main/jdk9/module-info.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2016, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +module org.glassfish.java.json { + requires transitive java.json; + exports org.glassfish.json.api; + provides javax.json.spi.JsonProvider with org.glassfish.json.JsonProviderImpl; +} diff --git a/impl/src/main/resources/org/glassfish/json/messages.properties b/impl/src/main/resources/org/glassfish/json/messages.properties new file mode 100644 index 00000000..959df2e2 --- /dev/null +++ b/impl/src/main/resources/org/glassfish/json/messages.properties @@ -0,0 +1,88 @@ +# +# Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. +# +# This program and the accompanying materials are made available under the +# terms of the Eclipse Public License v. 2.0, which is available at +# http://www.eclipse.org/legal/epl-2.0. +# +# This Source Code may also be made available under the following Secondary +# Licenses when the conditions for such availability set forth in the +# Eclipse Public License v. 2.0 are satisfied: GNU General Public License, +# version 2 with the GNU Classpath Exception, which is available at +# https://www.gnu.org/software/classpath/license.html. +# +# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 +# + +internal.error=Internal Error + +parser.getString.err=JsonParser#getString() is valid only for KEY_NAME, VALUE_STRING, VALUE_NUMBER parser states. \ + But current parser state is {0} +parser.isIntegralNumber.err=JsonParser#isIntegralNumber() is valid only for VALUE_NUMBER parser state. \ + But current parser state is {0} +parser.getInt.err=JsonParser#getInt() is valid only for VALUE_NUMBER parser state. \ + But current parser state is {0} +parser.getLong.err=JsonParser#getLong() is valid only for VALUE_NUMBER parser state. \ + But current parser state is {0} +parser.getBigDecimal.err=JsonParser#getBigDecimal() is valid only for VALUE_NUMBER parser state. \ + But current parser state is {0} +parser.getArray.err=JsonParser#getArray() or JsonParser#getArrayStream() is valid only for START_ARRAY parser state. \ + But current parser state is {0} +parser.getObject.err=JsonParser#getObject() or JsonParser#getObjectStream() is valid only for START_OBJECT parser state. \ + But current parser state is {0} +parser.getValue.err=JsonParser#getValue() is valid only for START_ARRAY, START_OBJECT, KEY_NAME, VALUE_STRING, VALUE_NUMBER, VALUE_NULL, VALUE_FALSE, VALUE_TRUE parser states. \ + But current parser state is {0} +parser.getValueStream.err=JsonParser#getValueStream() the parser must not be in an array or object. \ + But current parser state is {0} +parser.expected.eof=Expected EOF token, but got {0} +parser.tokenizer.close.io=I/O error while closing JSON tokenizer +parser.invalid.token=Invalid token={0} at {1}. Expected tokens are: {2} +parser.state.err=Unknown value type {0} +parser.scope.err=Cannot be called for value {0} +parser.input.enc.detect.failed=Cannot auto-detect encoding, not enough chars +parser.input.enc.detect.ioerr=I/O error while auto-detecting the encoding of stream + +generator.flush.io.err=I/O error while flushing generated JSON +generator.close.io.err=I/O error while closing JsonGenerator +generator.write.io.err=I/O error while writing in JsonGenerator +generator.illegal.method=Illegal method during JSON generation, \ + not valid in current context {0} +generator.double.infinite.nan=double value cannot be Infinite or NaN +generator.incomplete.json=Generating incomplete JSON +generator.illegal.multiple.text=Cannot generate more than one JSON text + +writer.write.already.called=write/writeObject/writeArray/close method is already called + +reader.read.already.called=read/readObject/readArray/close method is already called + +objbuilder.name.null=Name in JsonObject's name/value pair cannot be null +objbuilder.value.null=Value in JsonObject's name/value pair cannot be null +objbuilder.object.builder.null=Object builder that is used to create a value in JsonObject's name/value pair cannot be null +objbuilder.array.builder.null=Array builder that is used to create a value in JsonObject's name/value pair cannot be null + +arrbuilder.value.null=Cannot invoke add(null) while building JsonArray. +arrbuilder.object.builder.null=Object builder that is used to add a value to JSON array cannot be null +arrbuilder.array.builder.null=Array builder that is used to add a value to JSON array cannot be null +arrbuilder.valuelist.null=Index: {0}, Size: {1} + +tokenizer.unexpected.char=Unexpected char {0} at {1} +tokenizer.expected.char=Unexpected char {0} at {1}, expecting ''{2}'' +tokenizer.io.err=I/O error while parsing JSON + +pointer.format.invalid=A non-empty JSON Pointer must begin with a ''/'' +pointer.mapping.missing=The JSON Object ''{0}'' contains no mapping for the name ''{1}'' +pointer.reference.invalid=The reference value in a JSON Pointer must be a JSON Object or a JSON Array, was ''{0}'' +pointer.array.index.err=Array index format error, was ''{0}'' +pointer.array.index.illegal=Illegal integer format, was ''{0}'' + +noderef.value.add.err=The root value only allows adding a JSON object or array +noderef.value.cannot.remove=The JSON value at the root cannot be removed +noderef.object.missing=Non-existing name/value pair in the object for key {0} +noderef.array.index.err=An array item index is out of range. Index: {0}, Size: {1} + +patch.must.be.array=A JSON Patch must be an array of JSON Objects +patch.move.proper.prefix=The ''{0}'' path of the patch operation ''move'' is a proper prefix of the ''{1}'' path +patch.move.target.null=The ''{0}'' path of the patch operation ''move'' does not exist in target object +patch.test.failed=The JSON Patch operation ''test'' failed for path ''{0}'' and value ''{1}'' +patch.illegal.operation=Illegal value for the op member of the JSON Patch operation: ''{0}'' +patch.member.missing=The JSON Patch operation ''{0}'' must contain a ''{1}'' member diff --git a/jaxrs-1x/pom.xml b/jaxrs-1x/pom.xml new file mode 100644 index 00000000..d9597794 --- /dev/null +++ b/jaxrs-1x/pom.xml @@ -0,0 +1,121 @@ + + + + + 4.0.0 + + + org.glassfish + json + 1.2-SNAPSHOT + ../pom.xml + + + jsonp-jaxrs-1x + bundle + 1.2-SNAPSHOT + JSR 374 (JSON Processing) Media for JAX-RS 1.1 + JAX-RS 1.1 MessageBodyReader and MessageBodyWriter to support JsonObject/JsonArray/JsonStructure API of JSR 374:Java API for Processing JSON + https://javaee.github.io/jsonp + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + attach-javadocs + + jar + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + ${impl_version} + org.glassfish.json.jaxrs1x + + + + + + + + + javax.ws.rs + jsr311-api + provided + + + javax.json + javax.json-api + provided + + + + + + jdk9-setup + + 9 + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + default-compile + + + --add-modules + java.xml.ws.annotation + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + --add-modules + java.xml.ws.annotation + + + + + + + + + diff --git a/jaxrs-1x/src/main/java/org/glassfish/json/jaxrs1x/JsonStructureBodyReader.java b/jaxrs-1x/src/main/java/org/glassfish/json/jaxrs1x/JsonStructureBodyReader.java new file mode 100644 index 00000000..c5510729 --- /dev/null +++ b/jaxrs-1x/src/main/java/org/glassfish/json/jaxrs1x/JsonStructureBodyReader.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.jaxrs1x; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +import javax.json.Json; +import javax.json.JsonReader; +import javax.json.JsonReaderFactory; +import javax.json.JsonStructure; + +import javax.ws.rs.Consumes; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyReader; +import javax.ws.rs.ext.Provider; + +/** + * JAX-RS MessageBodyReader for JsonStructure. This allows + * JsonStructure, JsonArray and JsonObject to be a parameter of a + * resource method. + * + * @author Jitendra Kotamraju + * @author Blaise Doughan + * @author Michal Gajdos + */ +@Provider +@Consumes({"application/json", "text/json", "*/*"}) +public class JsonStructureBodyReader implements MessageBodyReader { + private final JsonReaderFactory rf = Json.createReaderFactory(null); + + private static final String JSON = "json"; + private static final String PLUS_JSON = "+json"; + + @Override + public boolean isReadable(Class aClass, Type type, + Annotation[] annotations, MediaType mediaType) { + return JsonStructure.class.isAssignableFrom(aClass) && supportsMediaType(mediaType); + } + + /** + * @return true for all media types of the pattern */json and + * */*+json. + */ + private static boolean supportsMediaType(final MediaType mediaType) { + return mediaType.getSubtype().equals(JSON) || mediaType.getSubtype().endsWith(PLUS_JSON); + } + + @Override + public JsonStructure readFrom(Class jsonStructureClass, + Type type, Annotation[] annotations, MediaType mediaType, + MultivaluedMap stringStringMultivaluedMap, + InputStream inputStream) throws IOException, WebApplicationException { + try (JsonReader reader = rf.createReader(inputStream)) { + return reader.read(); + } + } +} diff --git a/jaxrs-1x/src/main/java/org/glassfish/json/jaxrs1x/JsonStructureBodyWriter.java b/jaxrs-1x/src/main/java/org/glassfish/json/jaxrs1x/JsonStructureBodyWriter.java new file mode 100644 index 00000000..026ae090 --- /dev/null +++ b/jaxrs-1x/src/main/java/org/glassfish/json/jaxrs1x/JsonStructureBodyWriter.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.jaxrs1x; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; + +import javax.json.Json; +import javax.json.JsonStructure; +import javax.json.JsonWriter; +import javax.json.JsonWriterFactory; + +import javax.annotation.PostConstruct; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; + +/** + * JAX-RS MessageBodyWriter for JsonStructure. This allows + * JsonStructure, JsonArray and JsonObject to be return type of a + * resource method. + * + * @author Jitendra Kotamraju + * @author Blaise Doughan + * @author Michal Gajdos + */ +@Provider +@Produces({"application/json", "text/json", "*/*"}) +public class JsonStructureBodyWriter implements MessageBodyWriter { + private static final String JSON = "json"; + private static final String PLUS_JSON = "+json"; + + private JsonWriterFactory wf = Json.createWriterFactory(null); + + @PostConstruct + private void init() { + wf = Json.createWriterFactory(new HashMap<>()); + } + + @Override + public boolean isWriteable(Class aClass, + Type type, Annotation[] annotations, MediaType mediaType) { + return JsonStructure.class.isAssignableFrom(aClass) && supportsMediaType(mediaType); + } + + /** + * @return true for all media types of the pattern */json and + * */*+json. + */ + private static boolean supportsMediaType(final MediaType mediaType) { + return mediaType.getSubtype().equals(JSON) || mediaType.getSubtype().endsWith(PLUS_JSON); + } + + @Override + public long getSize(JsonStructure jsonStructure, Class aClass, + Type type, Annotation[] annotations, MediaType mediaType) { + + return -1; + } + + @Override + public void writeTo(JsonStructure jsonStructure, Class aClass, Type type, + Annotation[] annotations, MediaType mediaType, + MultivaluedMap stringObjectMultivaluedMap, + OutputStream outputStream) throws IOException, WebApplicationException { + try (JsonWriter writer = wf.createWriter(outputStream)) { + writer.write(jsonStructure); + } + } +} diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml new file mode 100644 index 00000000..fba0002c --- /dev/null +++ b/jaxrs/pom.xml @@ -0,0 +1,120 @@ + + + + + 4.0.0 + + + org.glassfish + json + 1.2-SNAPSHOT + ../pom.xml + + + org.glassfish + jsonp-jaxrs + bundle + 1.2-SNAPSHOT + JSR 374 (JSON Processing) Media for JAX-RS + JAX-RS MessageBodyReader and MessageBodyWriter to support JsonValue API of JSR 374:Java API for Processing JSON + https://javaee.github.io/jsonp + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + attach-javadocs + + jar + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + ${impl_version} + org.glassfish.json.jaxrs + + + + + + + + + javax.ws.rs + javax.ws.rs-api + + + javax.json + javax.json-api + provided + + + + + + jdk9-setup + + 9 + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + default-compile + + + --add-modules + java.xml.ws.annotation + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + --add-modules + java.xml.ws.annotation + + + + + + + + diff --git a/jaxrs/src/main/java/org/glassfish/json/jaxrs/JsonValueBodyReader.java b/jaxrs/src/main/java/org/glassfish/json/jaxrs/JsonValueBodyReader.java new file mode 100644 index 00000000..e04f7921 --- /dev/null +++ b/jaxrs/src/main/java/org/glassfish/json/jaxrs/JsonValueBodyReader.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.jaxrs; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import javax.json.Json; +import javax.json.JsonReader; +import javax.json.JsonReaderFactory; +import javax.json.JsonValue; +import javax.ws.rs.Consumes; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyReader; +import javax.ws.rs.ext.Provider; + +/** + * JAX-RS MessageBodyReader for JsonValue. This allows JsonValue + * to be a parameter of a resource method. + * + * @author Jitendra Kotamraju + * @author Blaise Doughan + * @author Michal Gajdos + */ +@Provider +@Consumes({"application/json", "text/json", "*/*"}) +public class JsonValueBodyReader implements MessageBodyReader { + private final JsonReaderFactory rf = Json.createReaderFactory(null); + + private static final String JSON = "json"; + private static final String PLUS_JSON = "+json"; + + @Override + public boolean isReadable(Class aClass, Type type, + Annotation[] annotations, MediaType mediaType) { + return JsonValue.class.isAssignableFrom(aClass) && supportsMediaType(mediaType); + } + + /** + * @return true for all media types of the pattern */json and + * */*+json. + */ + private static boolean supportsMediaType(final MediaType mediaType) { + return mediaType.getSubtype().equals(JSON) || mediaType.getSubtype().endsWith(PLUS_JSON); + } + + @Override + public JsonValue readFrom(Class jsonValueClass, + Type type, Annotation[] annotations, MediaType mediaType, + MultivaluedMap stringStringMultivaluedMap, + InputStream inputStream) throws IOException, WebApplicationException { + try (JsonReader reader = rf.createReader(inputStream)) { + return reader.readValue(); + } + } +} diff --git a/jaxrs/src/main/java/org/glassfish/json/jaxrs/JsonValueBodyWriter.java b/jaxrs/src/main/java/org/glassfish/json/jaxrs/JsonValueBodyWriter.java new file mode 100644 index 00000000..218ab2eb --- /dev/null +++ b/jaxrs/src/main/java/org/glassfish/json/jaxrs/JsonValueBodyWriter.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.jaxrs; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.PostConstruct; +import javax.json.Json; +import javax.json.JsonValue; +import javax.json.JsonWriter; +import javax.json.JsonWriterFactory; +import javax.json.stream.JsonGenerator; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Configuration; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; + +/** + * JAX-RS MessageBodyWriter for JsonValue. This allows JsonValue + * to be return type of a resource method. + * + * @author Jitendra Kotamraju + * @author Blaise Doughan + * @author Michal Gajdos + */ +@Provider +@Produces({"application/json", "text/json", "*/*"}) +public class JsonValueBodyWriter implements MessageBodyWriter { + private static final String JSON = "json"; + private static final String PLUS_JSON = "+json"; + + private JsonWriterFactory wf = Json.createWriterFactory(null); + + @Context + private Configuration config; + + @PostConstruct + private void init() { + Map props = new HashMap<>(); + if (config != null && config.getProperties().containsKey(JsonGenerator.PRETTY_PRINTING)) { + props.put(JsonGenerator.PRETTY_PRINTING, true); + } + wf = Json.createWriterFactory(props); + } + + @Override + public boolean isWriteable(Class aClass, + Type type, Annotation[] annotations, MediaType mediaType) { + return JsonValue.class.isAssignableFrom(aClass) && supportsMediaType(mediaType); + } + + /** + * @return true for all media types of the pattern */json and + * */*+json. + */ + private static boolean supportsMediaType(final MediaType mediaType) { + return mediaType.getSubtype().equals(JSON) || mediaType.getSubtype().endsWith(PLUS_JSON); + } + + @Override + public long getSize(JsonValue jsonValue, Class aClass, + Type type, Annotation[] annotations, MediaType mediaType) { + + return -1; + } + + @Override + public void writeTo(JsonValue jsonValue, Class aClass, Type type, + Annotation[] annotations, MediaType mediaType, + MultivaluedMap stringObjectMultivaluedMap, + OutputStream outputStream) throws IOException, WebApplicationException { + try (JsonWriter writer = wf.createWriter(outputStream)) { + writer.write(jsonValue); + } + } +} diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..9ca562b9 --- /dev/null +++ b/pom.xml @@ -0,0 +1,447 @@ + + + + + 4.0.0 + + + net.java + jvnet-parent + 5 + + + + 3.3.1 + + + org.glassfish + json + pom + 1.2-SNAPSHOT + JSR 374 (JSON Processing) RI + JSR 374:Java API for Processing JSON RI + https://javaee.github.io/jsonp + + + scm:git:git://github.com/eclipse-ee4j/jsonp.git + scm:git:git@github.com:eclipse-ee4j/jsonp.git + https://github.com/eclipse-ee4j/jsonp + HEAD + + + + + Eclipse Public License 2.0 + https://projects.eclipse.org/license/epl-2.0 + repo + + + GNU General Public License, version 2 with the GNU Classpath Exception + https://projects.eclipse.org/license/secondary-gpl-2.0-cp + repo + + + + + Oracle + http://www.oracle.com + + + + + lukasj + Lukas Jungmann + Oracle + + lead + + + + + + javax.json + org.glassfish + 1.1 + 1.2 + 1.1.2 + 1.1.2 + 1.2 + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.glassfish.copyright + glassfish-copyright-maven-plugin + + copyright.txt + copyright-exclude + git + true + false + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + verify + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-release-plugin + + forked-path + false + ${release.arguments} + + + + + + + + org.glassfish.build + spec-version-maven-plugin + 1.2 + + + org.glassfish.copyright + glassfish-copyright-maven-plugin + 1.46 + + + org.apache.felix + maven-bundle-plugin + 3.3.0 + + + org.codehaus.mojo + build-helper-maven-plugin + 3.0.0 + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.0.0-M1 + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + org.apache.maven.plugins + maven-compiler-plugin + 3.7.0 + + 1.8 + 1.8 + + -Xlint:all + + + + + org.apache.maven.plugins + maven-war-plugin + 3.1.0 + + + org.apache.maven.plugins + maven-jar-plugin + 3.0.2 + + + org.apache.maven.plugins + maven-dependency-plugin + 3.0.2 + + + org.apache.maven.plugins + maven-resources-plugin + 3.0.2 + + + org.codehaus.mojo + wagon-maven-plugin + 1.0 + + + org.apache.maven.plugins + maven-clean-plugin + 3.0.0 + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + + org.apache.maven.plugins + maven-install-plugin + 2.5.2 + + + org.apache.maven.plugins + maven-site-plugin + 3.6 + + + org.apache.maven.plugins + maven-surefire-plugin + 2.20 + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + + org.apache.maven.plugins + maven-assembly-plugin + 3.1.0 + + + org.apache.maven.plugins + maven-release-plugin + 2.5.3 + + + org.apache.maven.scm + maven-scm-provider-gitexe + 1.9.5 + + + + + + + + + + + javax.ws.rs + javax.ws.rs-api + 2.0.1 + provided + + + javax + javaee-web-api + 7.0 + + + javax.json + javax.json-api + ${project.version} + + + org.glassfish + java.json + ${project.version} + + + org.glassfish + javax.json + ${project.version} + + + javax.ws.rs + jsr311-api + 1.1.1 + + + junit + junit + 4.12 + test + + + + + + + release + + api + impl + jaxrs + + + + + jdk9-setup + + 9 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 8 + + + + default-compile + + 9 + 9 + 9 + + + + base-compile + + compile + + + + module-info.java + + + + + + + org.apache.felix + maven-bundle-plugin + + + <_failok>true + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-jdk9-source + generate-sources + + add-source + + + + src/main/jdk9 + + + + + + + + + + jdk9-all + + 9 + + + api + impl + jaxrs + jaxrs-1x + tests + gf + demos + bundles + + + + + all + + true + + + api + impl + jaxrs + jaxrs-1x + tests + gf + demos + bundles + + + + licensee + + api + impl + jaxrs + jaxrs-1x + tests + gf + demos + bundles + + + + jvnet-release + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.0.0-M1 + + + attach-javadocs + + jar + + + + + + + + + + + + + org.codehaus.mojo + findbugs-maven-plugin + 2.5.3 + + + + diff --git a/tests/pom.xml b/tests/pom.xml new file mode 100644 index 00000000..326e2809 --- /dev/null +++ b/tests/pom.xml @@ -0,0 +1,49 @@ + + + + + 4.0.0 + + + org.glassfish + json + 1.2-SNAPSHOT + ../pom.xml + + + jsonp-tests + jar + JSR 374 (JSON Processing) Tests + + + + javax.json + javax.json-api + + + org.glassfish + javax.json + test + + + junit + junit + + + diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonArrayTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonArrayTest.java new file mode 100644 index 00000000..72ed89b3 --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonArrayTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import junit.framework.TestCase; + +import javax.json.*; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.List; + +/** + * @author Jitendra Kotamraju + */ +public class JsonArrayTest extends TestCase { + public JsonArrayTest(String testName) { + super(testName); + } + + public void testArrayEquals() throws Exception { + JsonArray expected = Json.createArrayBuilder() + .add(JsonValue.TRUE) + .add(JsonValue.FALSE) + .add(JsonValue.NULL) + .add(Integer.MAX_VALUE) + .add(Long.MAX_VALUE) + .add(Double.MAX_VALUE) + .add(Integer.MIN_VALUE) + .add(Long.MIN_VALUE) + .add(Double.MIN_VALUE) + .build(); + + StringWriter sw = new StringWriter(); + JsonWriter writer = Json.createWriter(sw); + writer.writeArray(expected); + writer.close(); + + JsonReader reader = Json.createReader(new StringReader(sw.toString())); + JsonArray actual = reader.readArray(); + reader.close(); + + assertEquals(expected, actual); + } + + public void testStringValue() throws Exception { + JsonArray array = Json.createArrayBuilder() + .add("John") + .build(); + assertEquals("John", array.getString(0)); + } + + public void testIntValue() throws Exception { + JsonArray array = Json.createArrayBuilder() + .add(20) + .build(); + assertEquals(20, array.getInt(0)); + } + + public void testAdd() { + JsonArray array = Json.createArrayBuilder().build(); + try { + array.add(JsonValue.FALSE); + fail("JsonArray#add() should throw UnsupportedOperationException"); + } catch(UnsupportedOperationException e) { + // Expected + } + } + + public void testRemove() { + JsonArray array = Json.createArrayBuilder().build(); + try { + array.remove(0); + fail("JsonArray#remove() should throw UnsupportedOperationException"); + } catch(UnsupportedOperationException e) { + // Expected + } + } + + public void testNumberView() throws Exception { + JsonArray array = Json.createArrayBuilder().add(20).add(10).build(); + + List numberList = array.getValuesAs(JsonNumber.class); + for(JsonNumber num : numberList) { + num.intValue(); + } + + assertEquals(20, array.getInt(0)); + assertEquals(10, array.getInt(1)); + } + + public void testArrayBuilderNpe() { + try { + JsonArray array = Json.createArrayBuilder().add((JsonValue)null).build(); + fail("JsonArrayBuilder#add(null) should throw NullPointerException"); + } catch(NullPointerException e) { + // Expected + } + } + +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonBuilderFactoryTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonBuilderFactoryTest.java new file mode 100644 index 00000000..0ca9655b --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonBuilderFactoryTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import java.util.Map; +import javax.json.Json; +import javax.json.JsonArrayBuilder; +import javax.json.JsonBuilderFactory; +import javax.json.JsonObject; +import javax.json.JsonObjectBuilder; +import org.junit.Assert; +import org.junit.Test; + +/** + * + * @author lukas + */ +public class JsonBuilderFactoryTest { + + @Test + public void testArrayBuilder() { + JsonBuilderFactory builderFactory = Json.createBuilderFactory(null); + Assert.assertNotNull(builderFactory.createArrayBuilder()); + } + + @Test(expected = NullPointerException.class) + public void testArrayBuilderNPE() { + JsonBuilderFactory builderFactory = Json.createBuilderFactory(null); + builderFactory.createArrayBuilder(null); + } + + @Test + public void testArrayBuilderFromArray() { + JsonBuilderFactory builderFactory = Json.createBuilderFactory(null); + JsonArrayBuilder builder = builderFactory.createArrayBuilder(JsonBuilderTest.buildPhone()); + Assert.assertEquals(JsonBuilderTest.buildPhone(), builder.build()); + } + + @Test + public void testObjectBuilder() { + JsonBuilderFactory builderFactory = Json.createBuilderFactory(null); + Assert.assertNotNull(builderFactory.createObjectBuilder()); + } + + @Test(expected = NullPointerException.class) + public void testObjectBuilderNPE() { + JsonBuilderFactory builderFactory = Json.createBuilderFactory(null); + builderFactory.createObjectBuilder((JsonObject) null); + } + + @Test(expected = NullPointerException.class) + public void testObjectBuilderNPE_map() { + JsonBuilderFactory builderFactory = Json.createBuilderFactory(null); + builderFactory.createObjectBuilder((Map) null); + } + + @Test + public void testObjectBuilderFromObject() { + JsonBuilderFactory builderFactory = Json.createBuilderFactory(null); + JsonObjectBuilder builder = builderFactory.createObjectBuilder(JsonBuilderTest.buildPerson()); + Assert.assertEquals(JsonBuilderTest.buildPerson(), builder.build()); + } +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonBuilderTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonBuilderTest.java new file mode 100644 index 00000000..3263bcf9 --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonBuilderTest.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import junit.framework.TestCase; + +import javax.json.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +/** + * @author Jitendra Kotamraju + */ +public class JsonBuilderTest extends TestCase { + public JsonBuilderTest(String testName) { + super(testName); + } + + public void testEmptyObject() throws Exception { + JsonObject empty = Json.createObjectBuilder() + .build(); + + JsonObjectTest.testEmpty(empty); + } + + public void testEmptyArray() throws Exception { + JsonArray empty = Json.createArrayBuilder() + .build(); + + assertTrue(empty.isEmpty()); + } + + public void testObject() throws Exception { + JsonObject person = buildPerson(); + JsonObjectTest.testPerson(person); + } + + public void testNumber() throws Exception { + JsonObject person = buildPerson(); + JsonNumber number = person.getJsonNumber("age"); + assertEquals(25, number.intValueExact()); + assertEquals(25, number.intValue()); + assertTrue(number.isIntegral()); + JsonObjectTest.testPerson(person); + } + + public void testJsonObjectCopy() { + JsonObject person = buildPerson(); + final JsonObjectBuilder objectBuilder = Json.createObjectBuilder(person); + final JsonObject copyPerson = objectBuilder.build(); + + JsonNumber number = copyPerson.getJsonNumber("age"); + assertEquals(25, number.intValueExact()); + assertEquals(25, number.intValue()); + assertTrue(number.isIntegral()); + JsonObjectTest.testPerson(copyPerson); + + } + + public void testJsonObjectMap() { + Map person = buildPersonAsMap(); + final JsonObjectBuilder objectBuilder = Json.createObjectBuilder(person); + final JsonObject copyPerson = objectBuilder.build(); + + JsonNumber number = copyPerson.getJsonNumber("age"); + assertEquals(25, number.intValueExact()); + assertEquals(25, number.intValue()); + assertTrue(number.isIntegral()); + JsonObjectTest.testPerson(copyPerson); + } + + static Map buildPersonAsMap() { + Map person = new HashMap<>(); + person.put("firstName", "John"); + person.put("lastName", "Smith"); + person.put("age", 25); + + Map address = Optional.of(new HashMap()).get(); + address.put("streetAddress", "21 2nd Street"); + address.put("city", "New York"); + address.put("state", "NY"); + address.put("postalCode", "10021"); + + person.put("address", address); + person.put("mailingAddress", Optional.empty()); + + Collection> phones = new ArrayList<>(); + + Map phone1 = new HashMap<>(); + phone1.put("type", "home"); + phone1.put("number", "212 555-1234"); + phones.add(phone1); + + Map phone2 = new HashMap<>(); + phone2.put("type", "fax"); + phone2.put("number", "646 555-4567"); + phones.add(phone2); + + person.put("phoneNumber", phones); + + return person; + } + + static JsonObject buildPerson() { + return Json.createObjectBuilder() + .add("firstName", "John") + .add("lastName", "Smith") + .add("age", 25) + .add("address", Json.createObjectBuilder() + .add("streetAddress", "21 2nd Street") + .add("city", "New York") + .add("state", "NY") + .add("postalCode", "10021")) + .add("phoneNumber", Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("type", "home") + .add("number", "212 555-1234")) + .add(Json.createObjectBuilder() + .add("type", "fax") + .add("number", "646 555-4567"))) + .build(); + } + + static JsonObject buildAddress() { + return Json.createObjectBuilder() + .add("streetAddress", "21 2nd Street") + .add("city", "New York") + .add("state", "NY") + .add("postalCode", "10021") + .build(); + } + + static JsonArray buildPhone() { + return Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("type", "home") + .add("number", "212 555-1234")) + .add(Json.createObjectBuilder() + .add("type", "fax") + .add("number", "646 555-4567")) + .build(); + } + +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonCollectorTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonCollectorTest.java new file mode 100644 index 00000000..08855473 --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonCollectorTest.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import org.junit.BeforeClass; +import org.junit.Test; + +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.json.JsonPatchBuilder; +import javax.json.JsonValue; +import javax.json.stream.JsonCollectors; +import org.glassfish.json.JsonUtil; + +import static org.junit.Assert.assertEquals; + +/** + * Some JSON query tests/examples, using Java stream operations, with JSON collectors. + * @author Kin-man Chung + */ +public class JsonCollectorTest { + + static JsonArray contacts; + + @BeforeClass + public static void setUpClass() { + // The JSON source + contacts = (JsonArray) JsonUtil.toJson( + "[ " + + " { 'name': 'Duke', " + + " 'age': 18, " + + " 'gender': 'M', " + + " 'phones': { " + + " 'home': '650-123-4567', " + + " 'mobile': '650-234-5678'}}," + + " { 'name': 'Jane', " + + " 'age': 23, " + + " 'gender': 'F', " + + " 'phones': { " + + " 'mobile': '707-999-5555'}}," + + " { 'name': 'Joanna', " + + " 'gender': 'F', " + + " 'phones': { " + + " 'mobile': '505-333-4444'}} " + + " ]"); + } + + @Test + public void testToJsonArray() { + /* + * Query: retrieve the names of female contacts + * Returns a JsonArray of names + */ + JsonArray result = contacts.getValuesAs(JsonObject.class).stream() + .filter(x->"F".equals(x.getString("gender"))) + .map(x-> x.get("name")) + .collect(JsonCollectors.toJsonArray()); + JsonValue expected = JsonUtil.toJson("['Jane','Joanna']"); + assertEquals(expected, result); + } + + @Test + public void testToJsonObject() { + /* + * Query: retrieve the names and mobile phones of female contacts + * Returns a JsonObject of name phones pairs + */ + JsonObject result = contacts.getValuesAs(JsonObject.class).stream() + .filter(x->"F".equals(x.getString("gender"))) + .collect(JsonCollectors.toJsonObject( + x->x.asJsonObject().getString("name"), + x->x.asJsonObject().getJsonObject("phones").get("mobile"))) + ; + JsonValue expected = JsonUtil.toJson( + "{'Jane': '707-999-5555', 'Joanna': '505-333-4444'}"); + assertEquals(expected, result); + } + + @Test + public void testGroupBy() { + /* + * Query: group the contacts according to gender + * Returns a JsonObject, with gender/constacts value pairs + */ + JsonObject result = contacts.getValuesAs(JsonObject.class).stream() + .collect(JsonCollectors.groupingBy(x->((JsonObject)x).getString("gender"))); + JsonValue expected = JsonUtil.toJson( + "{'F': " + + " [ " + + " { 'name': 'Jane', " + + " 'age': 23, " + + " 'gender': 'F', " + + " 'phones': { " + + " 'mobile': '707-999-5555'}}," + + " { 'name': 'Joanna', " + + " 'gender': 'F', " + + " 'phones': { " + + " 'mobile': '505-333-4444'}} " + + " ], " + + "'M': " + + " [ " + + " { 'name': 'Duke', " + + " 'age': 18, " + + " 'gender': 'M', " + + " 'phones': { " + + " 'home': '650-123-4567', " + + " 'mobile': '650-234-5678'}} " + + " ] " + + "}"); + + assertEquals(result,expected); + } + + static int index; //for keeping track of the array index + @Test + public void testQueryAndPatch() { + /* + * Query and patch: Increment the ages of contacts with an age entry + * PatchBuilder is used for building the necessary JsonPatch. + */ + index = -1; + JsonPatchBuilder builder = Json.createPatchBuilder(); + contacts.getValuesAs(JsonObject.class).stream() + .peek(p->index++) + .filter(p->p.containsKey("age")) + .forEach(p-> builder.replace("/"+index+"/age", p.getInt("age")+1)); + JsonArray result = builder.build().apply(contacts); + + JsonValue expected = (JsonArray) JsonUtil.toJson( + "[ " + + " { 'name': 'Duke', " + + " 'age': 19, " + + " 'gender': 'M', " + + " 'phones': { " + + " 'home': '650-123-4567', " + + " 'mobile': '650-234-5678'}}," + + " { 'name': 'Jane', " + + " 'age': 24, " + + " 'gender': 'F', " + + " 'phones': { " + + " 'mobile': '707-999-5555'}}," + + " { 'name': 'Joanna', " + + " 'gender': 'F', " + + " 'phones': { " + + " 'mobile': '505-333-4444'}} " + + " ]"); + + assertEquals(expected, result); + } +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonFieldTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonFieldTest.java new file mode 100644 index 00000000..a3141ab2 --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonFieldTest.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import junit.framework.TestCase; + +import javax.json.Json; +import javax.json.JsonBuilderFactory; +import javax.json.JsonObject; +import javax.json.JsonObjectBuilder; +import javax.json.JsonValue; +import javax.json.stream.JsonGenerationException; +import javax.json.stream.JsonGenerator; +import java.io.StringWriter; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.concurrent.Callable; + +/** + * Test for writing json field names without values. + * + * @author Roman Grigoriadi + */ +public class JsonFieldTest extends TestCase { + + public void testFieldAsOnlyMember() { + StringWriter sw = new StringWriter(); + JsonGenerator generator = Json.createGenerator(sw); + + generator.writeStartObject(); + generator.writeKey("fName"); + generator.write("fValue"); + generator.writeEnd(); + + generator.close(); + assertEquals("{\"fName\":\"fValue\"}", sw.toString()); + } + + public void testFieldAsFirstMember() { + StringWriter sw = new StringWriter(); + JsonGenerator generator = Json.createGenerator(sw); + + generator.writeStartObject(); + generator.writeKey("f1Name"); + generator.write("f1Value"); + generator.write("f2Name", "f2Value"); + generator.writeEnd(); + + generator.close(); + assertEquals("{\"f1Name\":\"f1Value\",\"f2Name\":\"f2Value\"}", sw.toString()); + } + + public void testFieldAsLastMember() { + StringWriter sw = new StringWriter(); + JsonGenerator generator = Json.createGenerator(sw); + + generator.writeStartObject(); + generator.write("f1Name", "f1Value"); + generator.writeKey("f2Name"); + generator.write("f2Value"); + generator.writeEnd(); + + generator.close(); + assertEquals("{\"f1Name\":\"f1Value\",\"f2Name\":\"f2Value\"}", sw.toString()); + } + + + public void testFieldObject() { + StringWriter sw = new StringWriter(); + JsonGenerator generator = Json.createGenerator(sw); + + generator.writeStartObject(); + generator.writeKey("f1Name"); + generator.writeStartObject(); + generator.write("innerFieldName", "innerFieldValue"); + generator.writeEnd(); + generator.write("f2Name", "f2Value"); + generator.writeEnd(); + + generator.close(); + assertEquals("{\"f1Name\":{\"innerFieldName\":\"innerFieldValue\"},\"f2Name\":\"f2Value\"}", sw.toString()); + } + + public void testFieldArray() { + StringWriter sw = new StringWriter(); + JsonGenerator generator = Json.createGenerator(sw); + + generator.writeStartObject(); + generator.writeKey("f1Name"); + generator.writeStartArray(); + generator.write("arrayValue"); + generator.writeEnd(); + generator.write("f2Name", "f2Value"); + generator.writeEnd(); + + generator.close(); + assertEquals("{\"f1Name\":[\"arrayValue\"],\"f2Name\":\"f2Value\"}", sw.toString()); + } + + public void testFailFieldInField() { + StringWriter sw = new StringWriter(); + JsonGenerator generator = Json.createGenerator(sw); + + generator.writeStartObject(); + generator.writeKey("f1Name"); + + try { + generator.write("f2Name", "f2Value"); + fail("Field value, start object/array expected"); + } catch (JsonGenerationException exception) { + //ok + } + } + + + public void testFailFieldKeyInArray() { + StringWriter sw = new StringWriter(); + JsonGenerator generator = Json.createGenerator(sw); + + generator.writeStartArray(); + + try { + generator.writeKey("f1Value"); + fail("Not allowed in array ."); + } catch (JsonGenerationException exception) { + //ok + } + } + + public void testWriteString() { + assertEquals("{\"f1Name\":\"f1Value\"}", writeValue((gen)->gen.write("f1Value"))); + } + + public void testWriteBigDec() { + assertEquals("{\"f1Name\":10}", writeValue((gen)->gen.write(BigDecimal.TEN))); + } + + public void testWriteBigInt() { + assertEquals("{\"f1Name\":10}", writeValue((gen)->gen.write(BigInteger.TEN))); + } + + public void testWriteBool() { + assertEquals("{\"f1Name\":true}", writeValue((gen)->gen.write(true))); + } + + public void testWriteInt() { + assertEquals("{\"f1Name\":10}", writeValue((gen)->gen.write(10))); + } + + public void testWriteLong() { + assertEquals("{\"f1Name\":10}", writeValue((gen)->gen.write(10L))); + } + + public void testWriteDouble() { + assertEquals("{\"f1Name\":10.0}", writeValue((gen)->gen.write(10d))); + } + + public void testWriteNull() { + assertEquals("{\"f1Name\":null}", writeValue(JsonGenerator::writeNull)); + } + + public void testWriteJsonValue() { + JsonObjectBuilder builder = Json.createObjectBuilder(); + builder.add("first", "value"); + final JsonObject build = builder.build(); + assertEquals("{\"f1Name\":\"value\"}", writeValue((gen)->gen.write(build.getValue("/first")))); + } + + private String writeValue(WriteValueFunction writeValueCallback) { + StringWriter sw = new StringWriter(); + JsonGenerator generator = Json.createGenerator(sw); + + generator.writeStartObject(); + generator.writeKey("f1Name"); + writeValueCallback.writeValue(generator); + generator.writeEnd(); + generator.close(); + return sw.toString(); + } + + private interface WriteValueFunction { + void writeValue(JsonGenerator generator); + } +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonGeneratorFactoryTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonGeneratorFactoryTest.java new file mode 100644 index 00000000..de8b8a13 --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonGeneratorFactoryTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import junit.framework.TestCase; + +import javax.json.*; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonGeneratorFactory; +import java.io.StringWriter; +import java.util.HashMap; +import java.util.Map; + +/** + * Tests JsonGeneratorFactory + * + * @author Jitendra Kotamraju + */ +public class JsonGeneratorFactoryTest extends TestCase { + + public JsonGeneratorFactoryTest(String testName) { + super(testName); + } + + public void testGeneratorFactory() { + JsonGeneratorFactory generatorFactory = Json.createGeneratorFactory(null); + + JsonGenerator generator1 = generatorFactory.createGenerator(new StringWriter()); + generator1.writeStartArray().writeEnd(); + generator1.close(); + + JsonGenerator generator2 = generatorFactory.createGenerator(new StringWriter()); + generator2.writeStartArray().writeEnd(); + generator2.close(); + } + + public void testGeneratorFactoryWithConfig() { + Map config = new HashMap<>(); + config.put(JsonGenerator.PRETTY_PRINTING, true); + JsonGeneratorFactory generatorFactory = Json.createGeneratorFactory(config); + Map config1 = generatorFactory.getConfigInUse(); + if (config1.size() != 1) { + throw new JsonException("Expecting no of properties=1, got="+config1.size()); + } + assertTrue(config1.containsKey(JsonGenerator.PRETTY_PRINTING)); + + JsonGenerator generator1 = generatorFactory.createGenerator(new StringWriter()); + generator1.writeStartArray().writeEnd(); + generator1.close(); + + JsonGenerator generator2 = generatorFactory.createGenerator(new StringWriter()); + generator2.writeStartArray().writeEnd(); + generator2.close(); + } + +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonGeneratorTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonGeneratorTest.java new file mode 100644 index 00000000..754cb586 --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonGeneratorTest.java @@ -0,0 +1,519 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import junit.framework.TestCase; +import org.glassfish.json.api.BufferPool; + +import javax.json.*; +import javax.json.stream.*; +import java.io.*; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; + +/** + * {@link JsonGenerator} tests + * + * @author Jitendra Kotamraju + */ +public class JsonGeneratorTest extends TestCase { + public JsonGeneratorTest(String testName) { + super(testName); + } + + public void testObjectWriter() throws Exception { + StringWriter writer = new StringWriter(); + JsonGenerator generator = Json.createGenerator(writer); + testObject(generator); + generator.close(); + writer.close(); + + JsonReader reader = Json.createReader(new StringReader(writer.toString())); + JsonObject person = reader.readObject(); + JsonObjectTest.testPerson(person); + } + + public void testObjectStream() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + JsonGenerator generator = Json.createGenerator(out); + testObject(generator); + generator.close(); + out.close(); + + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + JsonReader reader = Json.createReader(in); + JsonObject person = reader.readObject(); + JsonObjectTest.testPerson(person); + reader.close(); + in.close(); + } + + static void testObject(JsonGenerator generator) throws Exception { + generator + .writeStartObject() + .write("firstName", "John") + .write("lastName", "Smith") + .write("age", 25) + .writeStartObject("address") + .write("streetAddress", "21 2nd Street") + .write("city", "New York") + .write("state", "NY") + .write("postalCode", "10021") + .writeEnd() + .writeStartArray("phoneNumber") + .writeStartObject() + .write("type", "home") + .write("number", "212 555-1234") + .writeEnd() + .writeStartObject() + .write("type", "fax") + .write("number", "646 555-4567") + .writeEnd() + .writeEnd() + .writeEnd(); + } + + public void testArray() throws Exception { + Writer sw = new StringWriter(); + JsonGenerator generator = Json.createGenerator(sw); + generator + .writeStartArray() + .writeStartObject() + .write("type", "home") + .write("number", "212 555-1234") + .writeEnd() + .writeStartObject() + .write("type", "fax") + .write("number", "646 555-4567") + .writeEnd() + .writeEnd(); + generator.close(); + } + + // tests JsonGenerator when JsonValue is used for generation + public void testJsonValue() throws Exception { + StringWriter writer = new StringWriter(); + JsonGenerator generator = Json.createGenerator(writer); + generator + .writeStartObject() + .write("firstName", "John") + .write("lastName", "Smith") + .write("age", 25) + .write("address", JsonBuilderTest.buildAddress()) + .write("phoneNumber", JsonBuilderTest.buildPhone()) + .writeEnd(); + generator.close(); + writer.close(); + + JsonReader reader = Json.createReader(new StringReader(writer.toString())); + JsonObject person = reader.readObject(); + JsonObjectTest.testPerson(person); + } + + public void testArrayString() throws Exception { + StringWriter writer = new StringWriter(); + JsonGenerator generator = Json.createGenerator(writer); + generator.writeStartArray().write("string").writeEnd(); + generator.close(); + writer.close(); + + assertEquals("[\"string\"]", writer.toString()); + } + + public void testEscapedString() throws Exception { + StringWriter writer = new StringWriter(); + JsonGenerator generator = Json.createGenerator(writer); + generator.writeStartArray().write("\u0000").writeEnd(); + generator.close(); + writer.close(); + + assertEquals("[\"\\u0000\"]", writer.toString()); + } + + public void testEscapedString1() throws Exception { + String expected = "\u0000\u00ff"; + StringWriter sw = new StringWriter(); + JsonGenerator generator = Json.createGenerator(sw); + generator.writeStartArray().write("\u0000\u00ff").writeEnd(); + generator.close(); + sw.close(); + + JsonReader jr = Json.createReader(new StringReader(sw.toString())); + JsonArray array = jr.readArray(); + String got = array.getString(0); + jr.close(); + + assertEquals(expected, got); + } + + public void testGeneratorEquals() throws Exception { + StringWriter sw = new StringWriter(); + JsonGenerator generator = Json.createGenerator(sw); + generator.writeStartArray() + .write(JsonValue.TRUE) + .write(JsonValue.FALSE) + .write(JsonValue.NULL) + .write(Integer.MAX_VALUE) + .write(Long.MAX_VALUE) + .write(Double.MAX_VALUE) + .write(Integer.MIN_VALUE) + .write(Long.MIN_VALUE) + .write(Double.MIN_VALUE) + .writeEnd(); + generator.close(); + + JsonReader reader = Json.createReader(new StringReader(sw.toString())); + JsonArray expected = reader.readArray(); + reader.close(); + + JsonArray actual = Json.createArrayBuilder() + .add(JsonValue.TRUE) + .add(JsonValue.FALSE) + .add(JsonValue.NULL) + .add(Integer.MAX_VALUE) + .add(Long.MAX_VALUE) + .add(Double.MAX_VALUE) + .add(Integer.MIN_VALUE) + .add(Long.MIN_VALUE) + .add(Double.MIN_VALUE) + .build(); + + assertEquals(expected, actual); + } + + public void testPrettyObjectWriter() throws Exception { + StringWriter writer = new StringWriter(); + Map config = new HashMap<>(); + config.put(JsonGenerator.PRETTY_PRINTING, true); + JsonGenerator generator = Json.createGeneratorFactory(config) + .createGenerator(writer); + testObject(generator); + generator.close(); + writer.close(); + + JsonReader reader = Json.createReader(new StringReader(writer.toString())); + JsonObject person = reader.readObject(); + JsonObjectTest.testPerson(person); + } + + public void testPrettyObjectStream() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Map config = new HashMap<>(); + config.put(JsonGenerator.PRETTY_PRINTING, true); + JsonGenerator generator = Json.createGeneratorFactory(config) + .createGenerator(out); + testObject(generator); + generator.close(); + out.close(); + + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + JsonReader reader = Json.createReader(in); + JsonObject person = reader.readObject(); + JsonObjectTest.testPerson(person); + reader.close(); + in.close(); + } + + public void testGenerationException1() throws Exception { + StringWriter writer = new StringWriter(); + JsonGenerator generator = Json.createGenerator(writer); + generator.writeStartObject(); + try { + generator.writeStartObject(); + fail("Expected JsonGenerationException, writeStartObject() cannot be called more than once"); + } catch (JsonGenerationException je) { + // Expected exception + } + } + + public void testGenerationException2() throws Exception { + StringWriter writer = new StringWriter(); + JsonGenerator generator = Json.createGenerator(writer); + generator.writeStartObject(); + try { + generator.writeStartArray(); + fail("Expected JsonGenerationException, writeStartArray() is valid in no context"); + } catch (JsonGenerationException je) { + // Expected exception + } + } + + public void testGenerationException3() throws Exception { + StringWriter writer = new StringWriter(); + JsonGenerator generator = Json.createGenerator(writer); + try { + generator.close(); + fail("Expected JsonGenerationException, no JSON is generated"); + } catch (JsonGenerationException je) { + // Expected exception + } + } + + public void testGenerationException4() throws Exception { + StringWriter writer = new StringWriter(); + JsonGenerator generator = Json.createGenerator(writer); + generator.writeStartArray(); + try { + generator.close(); + fail("Expected JsonGenerationException, writeEnd() is not called"); + } catch (JsonGenerationException je) { + // Expected exception + } + } + + public void testGenerationException5() throws Exception { + StringWriter writer = new StringWriter(); + JsonGenerator generator = Json.createGenerator(writer); + generator.writeStartObject(); + try { + generator.close(); + fail("Expected JsonGenerationException, writeEnd() is not called"); + } catch (JsonGenerationException je) { + // Expected exception + } + } + + public void testGenerationException6() throws Exception { + StringWriter writer = new StringWriter(); + JsonGenerator generator = Json.createGenerator(writer); + generator.writeStartObject().writeEnd(); + try { + generator.writeStartObject(); + fail("Expected JsonGenerationException, cannot generate one more JSON text"); + } catch (JsonGenerationException je) { + // Expected exception + } + } + + public void testGenerationException7() throws Exception { + StringWriter writer = new StringWriter(); + JsonGenerator generator = Json.createGenerator(writer); + generator.writeStartArray().writeEnd(); + try { + generator.writeStartArray(); + fail("Expected JsonGenerationException, cannot generate one more JSON text"); + } catch (JsonGenerationException je) { + // Expected exception + } + } + + + public void testGenerationException8() throws Exception { + StringWriter sWriter = new StringWriter(); + JsonGenerator generator = Json.createGenerator(sWriter); + generator.writeStartObject(); + try { + generator.write(JsonValue.TRUE); + fail("Expected JsonGenerationException, cannot generate one more JSON text"); + } catch (JsonGenerationException je) { + // Expected exception + } + } + + public void testGenerationException9() throws Exception { + StringWriter sWriter = new StringWriter(); + JsonGenerator generator = Json.createGenerator(sWriter); + generator.writeStartObject(); + try { + generator.write("name"); + fail("Expected JsonGenerationException, cannot generate one more JSON text"); + } catch (JsonGenerationException je) { + // Expected exception + } + } + + public void testGeneratorArrayDouble() throws Exception { + StringWriter writer = new StringWriter(); + JsonGenerator generator = Json.createGenerator(writer); + generator.writeStartArray(); + try { + generator.write(Double.NaN); + fail("JsonGenerator.write(Double.NaN) should produce NumberFormatException"); + } catch (NumberFormatException ne) { + // expected + } + try { + generator.write(Double.POSITIVE_INFINITY); + fail("JsonGenerator.write(Double.POSITIVE_INIFINITY) should produce NumberFormatException"); + } catch (NumberFormatException ne) { + // expected + } + try { + generator.write(Double.NEGATIVE_INFINITY); + fail("JsonGenerator.write(Double.NEGATIVE_INIFINITY) should produce NumberFormatException"); + } catch (NumberFormatException ne) { + // expected + } + generator.writeEnd(); + generator.close(); + } + + public void testGeneratorObjectDouble() throws Exception { + StringWriter writer = new StringWriter(); + JsonGenerator generator = Json.createGenerator(writer); + generator.writeStartObject(); + try { + generator.write("foo", Double.NaN); + fail("JsonGenerator.write(String, Double.NaN) should produce NumberFormatException"); + } catch (NumberFormatException ne) { + // expected + } + try { + generator.write("foo", Double.POSITIVE_INFINITY); + fail("JsonGenerator.write(String, Double.POSITIVE_INIFINITY) should produce NumberFormatException"); + } catch (NumberFormatException ne) { + // expected + } + try { + generator.write("foo", Double.NEGATIVE_INFINITY); + fail("JsonGenerator.write(String, Double.NEGATIVE_INIFINITY) should produce NumberFormatException"); + } catch (NumberFormatException ne) { + // expected + } + generator.writeEnd(); + generator.close(); + } + + public void testIntGenerator() throws Exception { + Random r = new Random(System.currentTimeMillis()); + JsonGeneratorFactory gf = Json.createGeneratorFactory(null); + JsonReaderFactory rf = Json.createReaderFactory(null); + JsonBuilderFactory bf = Json.createBuilderFactory(null); + for(int i=0; i < 100000; i++) { + int num = r.nextInt(); + StringWriter sw = new StringWriter(); + JsonGenerator generator = gf.createGenerator(sw); + generator.writeStartArray().write(num).writeEnd().close(); + + JsonReader reader = rf.createReader(new StringReader(sw.toString())); + JsonArray got = reader.readArray(); + reader.close(); + + JsonArray expected = bf.createArrayBuilder().add(num).build(); + + assertEquals(expected, got); + } + } + + public void testGeneratorBuf() throws Exception { + JsonGeneratorFactory gf = Json.createGeneratorFactory(null); + JsonReaderFactory rf = Json.createReaderFactory(null); + JsonBuilderFactory bf = Json.createBuilderFactory(null); + StringBuilder sb = new StringBuilder(); + int value = 10; + for(int i=0; i < 25000; i++) { + sb.append('a'); + String name = sb.toString(); + StringWriter sw = new StringWriter(); + JsonGenerator generator = gf.createGenerator(sw); + generator.writeStartObject().write(name, value).writeEnd().close(); + + JsonReader reader = rf.createReader(new StringReader(sw.toString())); + JsonObject got = reader.readObject(); + reader.close(); + + JsonObject expected = bf.createObjectBuilder().add(name, value).build(); + + assertEquals(expected, got); + } + } + + public void testBufferPoolFeature() { + final JsonParserTest.MyBufferPool bufferPool = new JsonParserTest.MyBufferPool(1024); + Map config = new HashMap() {{ + put(BufferPool.class.getName(), bufferPool); + }}; + + JsonGeneratorFactory factory = Json.createGeneratorFactory(config); + JsonGenerator generator = factory.createGenerator(new StringWriter()); + generator.writeStartArray(); + generator.writeEnd(); + generator.close(); + assertTrue(bufferPool.isTakeCalled()); + assertTrue(bufferPool.isRecycleCalled()); + } + + public void testBufferSizes() { + JsonReaderFactory rf = Json.createReaderFactory(null); + JsonBuilderFactory bf = Json.createBuilderFactory(null); + for(int size=10; size < 1000; size++) { + final JsonParserTest.MyBufferPool bufferPool = new JsonParserTest.MyBufferPool(size); + Map config = new HashMap() {{ + put(BufferPool.class.getName(), bufferPool); + }}; + JsonGeneratorFactory gf = Json.createGeneratorFactory(config); + + StringBuilder sb = new StringBuilder(); + int value = 10; + for(int i=0; i < 1500; i++) { + sb.append('a'); + String name = sb.toString(); + StringWriter sw = new StringWriter(); + JsonGenerator generator = gf.createGenerator(sw); + generator.writeStartObject().write(name, value).writeEnd().close(); + + JsonReader reader = rf.createReader(new StringReader(sw.toString())); + JsonObject got = reader.readObject(); + reader.close(); + + JsonObject expected = bf.createObjectBuilder().add(name, value).build(); + + assertEquals(expected, got); + } + + } + } + + public void testString() throws Exception { + escapedString(""); + escapedString("abc"); + escapedString("abc\f"); + escapedString("abc\na"); + escapedString("abc\tabc"); + escapedString("abc\n\tabc"); + escapedString("abc\n\tabc\r"); + escapedString("\n\tabc\r"); + escapedString("\bab\tb\rc\\\"\ftesting1234"); + escapedString("\f\babcdef\tb\rc\\\"\ftesting1234"); + } + + void escapedString(String expected) throws Exception { + StringWriter sw = new StringWriter(); + JsonGenerator generator = Json.createGenerator(sw); + generator.writeStartArray().write(expected).writeEnd(); + generator.close(); + sw.close(); + + JsonReader jr = Json.createReader(new StringReader(sw.toString())); + JsonArray array = jr.readArray(); + String got = array.getString(0); + jr.close(); + + assertEquals(expected, got); + } + + public void testFlush() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + JsonGenerator gen = Json.createGenerator(baos); + gen.writeStartObject().writeEnd(); + gen.flush(); + + assertEquals("{}", baos.toString("UTF-8")); + } + +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonMergePatchDiffTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonMergePatchDiffTest.java new file mode 100644 index 00000000..2ea6a0ba --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonMergePatchDiffTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.json.JsonReader; +import javax.json.JsonString; +import javax.json.JsonValue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +/** + * + * @author Alex Soto + * + */ +@RunWith(Parameterized.class) +public class JsonMergePatchDiffTest { + + @Parameters(name = "{index}: ({0})={1}") + public static Iterable data() throws Exception { + List examples = new ArrayList(); + JsonArray data = loadData(); + for (JsonValue jsonValue : data) { + JsonObject test = (JsonObject) jsonValue; + Object[] testData = new Object[4]; + testData[0] = test.get("original"); + testData[1] = test.get("target"); + testData[2] = test.get("expected"); + testData[3] = createExceptionClass((JsonString)test.get("exception")); + + examples.add(testData); + } + + return examples; + } + + private static Class createExceptionClass( + JsonString exceptionClassName) throws ClassNotFoundException { + if (exceptionClassName != null) { + return (Class) Class + .forName(exceptionClassName.getString()); + } + return null; + } + + private static JsonArray loadData() { + InputStream testData = JsonPatchTest.class + .getResourceAsStream("/jsonmergepatchdiff.json"); + JsonReader reader = Json.createReader(testData); + JsonArray data = (JsonArray) reader.read(); + return data; + } + + private JsonValue original; + private JsonValue target; + private JsonValue expected; + private Class expectedException; + + public JsonMergePatchDiffTest(JsonValue original, JsonValue target, + JsonValue expected, Class expectedException) { + super(); + this.original = original; + this.target = target; + this.expected = expected; + this.expectedException = expectedException; + } + @Test + public void shouldExecuteJsonMergePatchDiffOperationsToJsonDocument() { + try { + JsonValue output = Json.createMergeDiff(original, target).toJsonValue(); + assertThat(output, is(expected)); + assertThat(expectedException, nullValue()); + } catch (Exception e) { + if (expectedException == null) { + fail(e.getMessage()); + } else { + assertThat(e, instanceOf(expectedException)); + } + } + } +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonMergePatchTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonMergePatchTest.java new file mode 100644 index 00000000..7a81bf30 --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonMergePatchTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.json.JsonReader; +import javax.json.JsonString; +import javax.json.JsonValue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +/** + * + * @author Alex Soto + * + */ +@RunWith(Parameterized.class) +public class JsonMergePatchTest { + + @Parameters(name = "{index}: ({0})={1}") + public static Iterable data() throws Exception { + List examples = new ArrayList(); + JsonArray data = loadData(); + for (JsonValue jsonValue : data) { + JsonObject test = (JsonObject) jsonValue; + Object[] testData = new Object[4]; + testData[0] = test.get("patch"); + testData[1] = test.get("target"); + testData[2] = test.get("expected"); + testData[3] = createExceptionClass((JsonString)test.get("exception")); + + examples.add(testData); + } + + return examples; + } + + private static Class createExceptionClass( + JsonString exceptionClassName) throws ClassNotFoundException { + if (exceptionClassName != null) { + return (Class) Class + .forName(exceptionClassName.getString()); + } + return null; + } + + private static JsonArray loadData() { + InputStream testData = JsonPatchTest.class + .getResourceAsStream("/jsonmergepatch.json"); + JsonReader reader = Json.createReader(testData); + JsonArray data = (JsonArray) reader.read(); + return data; + } + + private JsonValue patch; + private JsonValue target; + private JsonValue expected; + private Class expectedException; + + public JsonMergePatchTest(JsonValue patch, JsonValue target, + JsonValue expected, Class expectedException) { + super(); + this.patch = patch; + this.target = target; + this.expected = expected; + this.expectedException = expectedException; + } + + @Test + public void shouldExecuteJsonMergePatchDiffOperationsToJsonDocument() { + try { + JsonValue output = Json.createMergePatch(patch).apply(target); + assertThat(output, is(expected)); + assertThat(expectedException, nullValue()); + } catch (Exception e) { + if (expectedException == null) { + fail(e.getMessage()); + } else { + assertThat(e, instanceOf(expectedException)); + } + } + } + +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonNumberTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonNumberTest.java new file mode 100644 index 00000000..e5cc5c43 --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonNumberTest.java @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import junit.framework.TestCase; + +import javax.json.*; +import java.io.StringReader; +import java.io.StringWriter; +import java.math.BigDecimal; +import java.math.BigInteger; + +/** + * @author Jitendra Kotamraju + */ +public class JsonNumberTest extends TestCase { + public JsonNumberTest(String testName) { + super(testName); + } + + public void testFloating() throws Exception { + JsonArray array1 = Json.createArrayBuilder().add(10.4).build(); + JsonReader reader = Json.createReader(new StringReader("[10.4]")); + JsonArray array2 = reader.readArray(); + + assertEquals(array1.get(0), array2.get(0)); + assertEquals(array1, array2); + } + + public void testBigDecimal() throws Exception { + JsonArray array1 = Json.createArrayBuilder().add(new BigDecimal("10.4")).build(); + JsonReader reader = Json.createReader(new StringReader("[10.4]")); + JsonArray array2 = reader.readArray(); + + assertEquals(array1.get(0), array2.get(0)); + assertEquals(array1, array2); + } + + public void testIntNumberType() throws Exception { + JsonArray array1 = Json.createArrayBuilder() + .add(Integer.MIN_VALUE) + .add(Integer.MAX_VALUE) + .add(Integer.MIN_VALUE + 1) + .add(Integer.MAX_VALUE - 1) + .add(12) + .add(12l) + .add(new BigInteger("0")) + .build(); + testNumberType(array1, true); + + StringReader sr = new StringReader("[" + + "-2147483648, " + + "2147483647, " + + "-2147483647, " + + "2147483646, " + + "12, " + + "12, " + + "0 " + + "]"); + JsonReader reader = Json.createReader(sr); + JsonArray array2 = reader.readArray(); + reader.close(); + testNumberType(array2, true); + + assertEquals(array1, array2); + } + + private void testNumberType(JsonArray array, boolean integral) { + for (JsonValue value : array) { + assertEquals(integral, ((JsonNumber) value).isIntegral()); + } + } + + public void testLongNumberType() throws Exception { + JsonArray array1 = Json.createArrayBuilder() + .add(Long.MIN_VALUE) + .add(Long.MAX_VALUE) + .add(Long.MIN_VALUE + 1) + .add(Long.MAX_VALUE - 1) + .add((long) Integer.MIN_VALUE - 1) + .add((long) Integer.MAX_VALUE + 1) + .build(); + testNumberType(array1, true); + + StringReader sr = new StringReader("[" + + "-9223372036854775808, " + + "9223372036854775807, " + + "-9223372036854775807, " + + "9223372036854775806, " + + "-2147483649, " + + "2147483648 " + + "]"); + JsonReader reader = Json.createReader(sr); + JsonArray array2 = reader.readArray(); + reader.close(); + testNumberType(array2, true); + + assertEquals(array1, array2); + } + + +// public void testBigIntegerNumberType() throws Exception { +// JsonArray array1 = new JsonBuilder() +// .startArray() +// .add(new BigInteger("-9223372036854775809")) +// .add(new BigInteger("9223372036854775808")) +// .add(new BigInteger("012345678901234567890")) +// .end() +// .build(); +// testNumberType(array1, JsonNumber.NumberType.BIG_INTEGER); +// +// StringReader sr = new StringReader("[" + +// "-9223372036854775809, " + +// "9223372036854775808, " + +// "12345678901234567890 " + +// "]"); +// JsonReader reader = new JsonReader(sr); +// JsonArray array2 = reader.readArray(); +// reader.close(); +// testNumberType(array2, JsonNumber.NumberType.BIG_INTEGER); +// +// assertEquals(array1, array2); +// } + + public void testBigDecimalNumberType() throws Exception { + JsonArray array1 = Json.createArrayBuilder() + .add(12d) + .add(12.0d) + .add(12.1d) + .add(Double.MIN_VALUE) + .add(Double.MAX_VALUE) + .build(); + testNumberType(array1, false); + + StringReader sr = new StringReader("[" + + "12.0, " + + "12.0, " + + "12.1, " + + "4.9E-324, " + + "1.7976931348623157E+308 " + + "]"); + JsonReader reader = Json.createReader(sr); + JsonArray array2 = reader.readArray(); + reader.close(); + testNumberType(array2, false); + + assertEquals(array1, array2); + } + + public void testMinMax() throws Exception { + JsonArray expected = Json.createArrayBuilder() + .add(Integer.MIN_VALUE) + .add(Integer.MAX_VALUE) + .add(Long.MIN_VALUE) + .add(Long.MAX_VALUE) + .add(Double.MIN_VALUE) + .add(Double.MAX_VALUE) + .build(); + + StringWriter sw = new StringWriter(); + JsonWriter writer = Json.createWriter(sw); + writer.writeArray(expected); + writer.close(); + + JsonReader reader = Json.createReader(new StringReader(sw.toString())); + JsonArray actual = reader.readArray(); + reader.close(); + + assertEquals(expected, actual); + } + + public void testLeadingZeroes() { + JsonArray array = Json.createArrayBuilder() + .add(0012.1d) + .build(); + + StringWriter sw = new StringWriter(); + JsonWriter jw = Json.createWriter(sw); + jw.write(array); + jw.close(); + + assertEquals("[12.1]", sw.toString()); + } + + public void testBigIntegerExact() { + try { + JsonArray array = Json.createArrayBuilder().add(12345.12345).build(); + array.getJsonNumber(0).bigIntegerValueExact(); + fail("Expected Arithmetic exception"); + } catch (ArithmeticException expected) { + // no-op + } + } + + +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonObjectTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonObjectTest.java new file mode 100644 index 00000000..2c155184 --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonObjectTest.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import junit.framework.TestCase; + +import javax.json.*; + +/** + * @author Jitendra Kotamraju + */ +public class JsonObjectTest extends TestCase { + public JsonObjectTest(String testName) { + super(testName); + } + + public void test() { + } + + public void testEmptyObjectEquals() throws Exception { + JsonObject empty1 = Json.createObjectBuilder() + .build(); + + JsonObject empty2 = Json.createObjectBuilder() + .build(); + + assertEquals(empty1, empty2); + } + + public void testPersonObjectEquals() throws Exception { + JsonObject person1 = JsonBuilderTest.buildPerson(); + JsonObject person2 = JsonReaderTest.readPerson(); + + assertEquals(person1, person2); + } + + static void testPerson(JsonObject person) { + assertEquals(5, person.size()); + assertEquals("John", person.getString("firstName")); + assertEquals("Smith", person.getString("lastName")); + assertEquals(25, person.getJsonNumber("age").intValue()); + assertEquals(25, person.getInt("age")); + + JsonObject address = person.getJsonObject("address"); + assertEquals(4, address.size()); + assertEquals("21 2nd Street", address.getString("streetAddress")); + assertEquals("New York", address.getString("city")); + assertEquals("NY", address.getString("state")); + assertEquals("10021", address.getString("postalCode")); + + JsonArray phoneNumber = person.getJsonArray("phoneNumber"); + assertEquals(2, phoneNumber.size()); + JsonObject home = phoneNumber.getJsonObject(0); + assertEquals(2, home.size()); + assertEquals("home", home.getString("type")); + assertEquals("212 555-1234", home.getString("number")); + assertEquals("212 555-1234", home.getString("number")); + + JsonObject fax = phoneNumber.getJsonObject(1); + assertEquals(2, fax.size()); + assertEquals("fax", fax.getString("type")); + assertEquals("646 555-4567", fax.getString("number")); + + assertEquals("\"646 555-4567\"", fax.getJsonString("number").toString()); + } + + static void testEmpty(JsonObject empty) { + assertTrue(empty.isEmpty()); + } + + public void testClassCastException() { + JsonObject obj = Json.createObjectBuilder() + .add("foo", JsonValue.FALSE).build(); + try { + obj.getJsonNumber("foo"); + fail("Expected ClassCastException for casting JsonValue.FALSE to JsonNumber"); + } catch (ClassCastException ce) { + // Expected + } + } + + public void testPut() { + JsonObject obj = Json.createObjectBuilder().add("foo", 1).build(); + try { + obj.put("bar", JsonValue.FALSE); + fail("JsonObject#put() should throw UnsupportedOperationException"); + } catch(UnsupportedOperationException e) { + // Expected + } + } + + public void testRemove() { + JsonObject obj = Json.createObjectBuilder().add("foo", 1).build(); + try { + obj.remove("foo"); + fail("JsonObject#remove() should throw UnsupportedOperationException"); + } catch(UnsupportedOperationException e) { + // Expected + } + } + + public void testObjectBuilderNpe() { + try { + JsonObject obj = Json.createObjectBuilder().add(null, 1).build(); + fail("JsonObjectBuilder#add(null, 1) should throw NullPointerException"); + } catch(NullPointerException e) { + // Expected + } + } + +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonParserFactoryTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonParserFactoryTest.java new file mode 100644 index 00000000..8e8753bd --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonParserFactoryTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import junit.framework.TestCase; + +import javax.json.Json; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParserFactory; +import java.io.StringReader; +import java.util.HashMap; +import java.util.Map; + +/** + * Tests JsonParserFactory + * + * @author Jitendra Kotamraju + */ +public class JsonParserFactoryTest extends TestCase { + + public JsonParserFactoryTest(String testName) { + super(testName); + } + + public void testParserFactory() { + JsonParserFactory parserFactory = Json.createParserFactory(null); + JsonParser parser1 = parserFactory.createParser(new StringReader("[]")); + parser1.close(); + JsonParser parser2 = parserFactory.createParser(new StringReader("[]")); + parser2.close(); + } + + public void testParserFactoryWithConfig() { + Map config = new HashMap<>(); + JsonParserFactory parserFactory = Json.createParserFactory(config); + JsonParser parser1 = parserFactory.createParser(new StringReader("[]")); + parser1.close(); + JsonParser parser2 = parserFactory.createParser(new StringReader("[]")); + parser2.close(); + } + +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonParserSkipTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonParserSkipTest.java new file mode 100644 index 00000000..a44e2141 --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonParserSkipTest.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import java.io.StringReader; +import javax.json.Json; +import javax.json.stream.JsonParser; +import junit.framework.TestCase; +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertTrue; + +/** + * + * @author lukas + */ +public class JsonParserSkipTest extends TestCase { + + public void testSkipArrayReader() { + try (JsonParser parser = Json.createParser(new StringReader("[[],[[]]]"))) { + testSkipArray(parser); + } + } + + public void testSkipArrayStructure() { + try (JsonParser parser = Json.createParserFactory(null).createParser( + Json.createArrayBuilder() + .add(Json.createArrayBuilder()) + .add(Json.createArrayBuilder() + .add(Json.createArrayBuilder())) + .build())) { + testSkipArray(parser); + } + } + + private static void testSkipArray(JsonParser parser) { + assertEquals(JsonParser.Event.START_ARRAY, parser.next()); + parser.skipArray(); + assertEquals(false, parser.hasNext()); + } + + public void testSkipArrayInObjectReader() { + try (JsonParser parser = Json.createParser(new StringReader("{\"array\":[[],[[]]],\"object\":\"value2\"}"))) { + testSkipArrayInObject(parser); + } + } + + public void testSkipArrayInObjectStructure() { + try (JsonParser parser = Json.createParserFactory(null).createParser( + Json.createObjectBuilder().add("array", Json.createArrayBuilder() + .add(Json.createArrayBuilder()) + .add(Json.createArrayBuilder() + .add(Json.createArrayBuilder())) + ).add("object", "value2") + .build())) { + testSkipArrayInObject(parser); + } + } + + private static void testSkipArrayInObject(JsonParser parser) { + assertEquals(JsonParser.Event.START_OBJECT, parser.next()); + assertEquals(JsonParser.Event.KEY_NAME, parser.next()); + assertEquals(JsonParser.Event.START_ARRAY, parser.next()); + parser.skipArray(); + assertTrue(parser.hasNext()); + assertEquals(JsonParser.Event.KEY_NAME, parser.next()); + assertEquals(JsonParser.Event.VALUE_STRING, parser.next()); + assertEquals(JsonParser.Event.END_OBJECT, parser.next()); + assertFalse(parser.hasNext()); + } + + public void testSkipObjectReader() { + try (JsonParser parser = Json.createParser(new StringReader("{\"array\":[],\"objectToSkip\":{\"huge key\":\"huge value\"},\"simple\":2}"))) { + testSkipObject(parser); + } + } + + public void testSkipObjectStructure() { + try (JsonParser parser = Json.createParserFactory(null).createParser( + Json.createObjectBuilder() + .add("array", Json.createArrayBuilder().build()) + .add("objectToSkip", Json.createObjectBuilder().add("huge key", "huge value")) + .add("simple", 2) + .build())) { + testSkipObject(parser); + } + } + + private static void testSkipObject(JsonParser parser) { + assertEquals(JsonParser.Event.START_OBJECT, parser.next()); + assertEquals(JsonParser.Event.KEY_NAME, parser.next()); + assertEquals(JsonParser.Event.START_ARRAY, parser.next()); + assertEquals(JsonParser.Event.END_ARRAY, parser.next()); + assertEquals(JsonParser.Event.KEY_NAME, parser.next()); + assertEquals(JsonParser.Event.START_OBJECT, parser.next()); + parser.skipObject(); + assertEquals(JsonParser.Event.KEY_NAME, parser.next()); + assertEquals(JsonParser.Event.VALUE_NUMBER, parser.next()); + assertEquals(JsonParser.Event.END_OBJECT, parser.next()); + assertEquals(false, parser.hasNext()); + } +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonParserTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonParserTest.java new file mode 100644 index 00000000..0f6ca533 --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonParserTest.java @@ -0,0 +1,766 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; +import junit.framework.TestCase; + +import javax.json.*; +import javax.json.stream.JsonLocation; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; +import javax.json.stream.JsonParserFactory; +import java.math.BigDecimal; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Random; +import java.util.Scanner; + +import org.glassfish.json.api.BufferPool; + +/** + * JsonParser Tests + * + * @author Jitendra Kotamraju + */ +public class JsonParserTest extends TestCase { + static final Charset UTF_32LE = Charset.forName("UTF-32LE"); + static final Charset UTF_32BE = Charset.forName("UTF-32BE"); + + public JsonParserTest(String testName) { + super(testName); + } + + public void testReader() { + JsonParser reader = Json.createParser( + new StringReader("{ \"a\" : \"b\", \"c\" : null, \"d\" : [null, \"abc\"] }")); + reader.close(); + } + + + public void testEmptyArrayReader() { + try (JsonParser parser = Json.createParser(new StringReader("[]"))) { + testEmptyArray(parser); + } + } + + public void testEmptyArrayStream() { + try (JsonParser parser = Json.createParser( + new ByteArrayInputStream(new byte[]{'[', ']'}))) { + testEmptyArray(parser); + } + } + + public void testEmptyArrayStreamUTF8() { + ByteArrayInputStream bin = new ByteArrayInputStream("[]".getBytes(StandardCharsets.UTF_8)); + try (JsonParser parser = Json.createParser(bin)) { + testEmptyArray(parser); + } + } + + public void testEmptyArrayStreamUTF16LE() { + ByteArrayInputStream bin = new ByteArrayInputStream("[]".getBytes(StandardCharsets.UTF_16LE)); + try (JsonParser parser = Json.createParser(bin)) { + testEmptyArray(parser); + } + } + + public void testEmptyArrayStreamUTF16BE() { + ByteArrayInputStream bin = new ByteArrayInputStream("[]".getBytes(StandardCharsets.UTF_16BE)); + try (JsonParser parser = Json.createParser(bin)) { + testEmptyArray(parser); + } + } + + public void testEmptyArrayStreamUTF32LE() { + ByteArrayInputStream bin = new ByteArrayInputStream("[]".getBytes(UTF_32LE)); + try (JsonParser parser = Json.createParser(bin)) { + testEmptyArray(parser); + } + } + + public void testEmptyArrayStreamUTF32BE() { + ByteArrayInputStream bin = new ByteArrayInputStream("[]".getBytes(UTF_32BE)); + try (JsonParser parser = Json.createParser(bin)) { + testEmptyArray(parser); + } + } + + public void testEmptyArrayStreamUTF16() { + ByteArrayInputStream bin = new ByteArrayInputStream("[]".getBytes(StandardCharsets.UTF_16)); + try (JsonParser parser = Json.createParser(bin)) { + testEmptyArray(parser); + } + } + + public void testEmptyArrayStreamWithConfig() { + Map config = new HashMap<>(); + try (JsonParser parser = Json.createParserFactory(config).createParser( + new ByteArrayInputStream(new byte[]{'[', ']'}))) { + testEmptyArray(parser); + } + } + + public void testEmptyArrayStructure() { + try (JsonParser parser = Json.createParserFactory(null).createParser( + Json.createArrayBuilder().build())) { + testEmptyArray(parser); + } + } + + public void testEmptyArrayStructureWithConfig() { + Map config = new HashMap<>(); + try (JsonParser parser = Json.createParserFactory(config).createParser( + Json.createArrayBuilder().build())) { + testEmptyArray(parser); + } + } + + @SuppressWarnings("UnusedDeclaration") + static void testEmptyArray(JsonParser parser) { + while (parser.hasNext()) { + parser.next(); + } + } + + + public void testEmptyArrayReaderIterator() { + try (JsonParser parser = Json.createParser(new StringReader("[]"))) { + testEmptyArrayIterator(parser); + } + } + + public void testEmptyArrayStructureIterator() { + try (JsonParser parser = Json.createParserFactory(null).createParser( + Json.createArrayBuilder().build())) { + testEmptyArrayIterator(parser); + } + } + + static void testEmptyArrayIterator(JsonParser parser) { + assertEquals(true, parser.hasNext()); + assertEquals(true, parser.hasNext()); + assertEquals(Event.START_ARRAY, parser.next()); + + assertEquals(true, parser.hasNext()); + assertEquals(true, parser.hasNext()); + assertEquals(Event.END_ARRAY, parser.next()); + + assertEquals(false, parser.hasNext()); + assertEquals(false, parser.hasNext()); + try { + parser.next(); + fail("Should have thrown a NoSuchElementException"); + } catch (NoSuchElementException ne) { + } + } + + + public void testEmptyArrayIterator2Reader() { + try (JsonParser parser = Json.createParser(new StringReader("[]"))) { + testEmptyArrayIterator2(parser); + } + } + + public void testEmptyArrayIterator2Structure() { + try (JsonParser parser = Json.createParserFactory(null).createParser( + Json.createArrayBuilder().build())) { + testEmptyArrayIterator2(parser); + } + } + + static void testEmptyArrayIterator2(JsonParser parser) { + assertEquals(Event.START_ARRAY, parser.next()); + assertEquals(Event.END_ARRAY, parser.next()); + try { + parser.next(); + fail("Should have thrown a NoSuchElementException"); + } catch (NoSuchElementException ne) { + } + } + + public void testEmptyArrayIterator3Reader() { + try (JsonParser parser = Json.createParser(new StringReader("[]"))) { + testEmptyArrayIterator3(parser); + } + } + + public void testEmptyArrayIterator3Structure() { + try (JsonParser parser = Json.createParserFactory(null).createParser( + Json.createArrayBuilder().build())) { + testEmptyArrayIterator3(parser); + } + } + + static void testEmptyArrayIterator3(JsonParser parser) { + assertEquals(Event.START_ARRAY, parser.next()); + assertEquals(Event.END_ARRAY, parser.next()); + assertEquals(false, parser.hasNext()); + try { + parser.next(); + fail("Should have thrown a NoSuchElementException"); + } catch (NoSuchElementException ne) { + } + } + + + // Tests empty object + public void testEmptyObjectReader() { + try (JsonParser parser = Json.createParser(new StringReader("{}"))) { + testEmptyObject(parser); + } + } + + public void testEmptyObjectStream() { + try (JsonParser parser = Json.createParser( + new ByteArrayInputStream(new byte[]{'{', '}'}))) { + testEmptyObject(parser); + } + } + + public void testEmptyObjectStructure() { + try (JsonParser parser = Json.createParserFactory(null).createParser( + Json.createObjectBuilder().build())) { + testEmptyObject(parser); + } + } + + public void testEmptyObjectStructureWithConfig() { + Map config = new HashMap<>(); + try (JsonParser parser = Json.createParserFactory(config).createParser( + Json.createObjectBuilder().build())) { + testEmptyObject(parser); + } + } + + @SuppressWarnings("UnusedDeclaration") + static void testEmptyObject(JsonParser parser) { + while (parser.hasNext()) { + parser.next(); + } + } + + + public void testEmptyObjectIteratorReader() { + try (JsonParser parser = Json.createParser(new StringReader("{}"))) { + testEmptyObjectIterator(parser); + } + } + + public void testEmptyObjectIteratorStructure() { + try (JsonParser parser = Json.createParserFactory(null).createParser( + Json.createObjectBuilder().build())) { + testEmptyObjectIterator(parser); + } + } + + static void testEmptyObjectIterator(JsonParser parser) { + assertEquals(true, parser.hasNext()); + assertEquals(true, parser.hasNext()); + assertEquals(Event.START_OBJECT, parser.next()); + + assertEquals(true, parser.hasNext()); + assertEquals(true, parser.hasNext()); + assertEquals(Event.END_OBJECT, parser.next()); + + assertEquals(false, parser.hasNext()); + assertEquals(false, parser.hasNext()); + try { + parser.next(); + fail("Should have thrown a NoSuchElementException"); + } catch (NoSuchElementException ne) { + } + } + + + public void testEmptyObjectIterator2Reader() { + try (JsonParser parser = Json.createParser(new StringReader("{}"))) { + testEmptyObjectIterator2(parser); + } + } + + public void testEmptyObjectIterator2Structure() { + try (JsonParser parser = Json.createParserFactory(null).createParser( + Json.createObjectBuilder().build())) { + testEmptyObjectIterator2(parser); + } + } + + static void testEmptyObjectIterator2(JsonParser parser) { + assertEquals(Event.START_OBJECT, parser.next()); + assertEquals(Event.END_OBJECT, parser.next()); + try { + parser.next(); + fail("Should have thrown a NoSuchElementException"); + } catch (NoSuchElementException ne) { + } + } + + + public void testEmptyObjectIterator3Reader() { + try (JsonParser parser = Json.createParser(new StringReader("{}"))) { + testEmptyObjectIterator3(parser); + } + } + + public void testEmptyObjectIterator3Structure() { + try (JsonParser parser = Json.createParserFactory(null).createParser( + Json.createObjectBuilder().build())) { + testEmptyObjectIterator3(parser); + } + } + + static void testEmptyObjectIterator3(JsonParser parser) { + assertEquals(Event.START_OBJECT, parser.next()); + assertEquals(Event.END_OBJECT, parser.next()); + assertEquals(false, parser.hasNext()); + try { + parser.next(); + fail("Should have thrown a NoSuchElementException"); + } catch (NoSuchElementException ne) { + // expected + } + } + + + public void testWikiIteratorReader() throws Exception { + try (JsonParser parser = Json.createParser(wikiReader())) { + testWikiIterator(parser); + } + } + + public void testWikiIteratorStructure() throws Exception { + try (JsonParser parser = Json.createParserFactory(null).createParser( + JsonBuilderTest.buildPerson())) { + testWikiIterator(parser); + } + } + + @SuppressWarnings("UnusedDeclaration") + static void testWikiIterator(JsonParser parser) throws Exception { + while (parser.hasNext()) { + parser.next(); + } + } + + public void testWikiInputStream() throws Exception { + try (JsonParser parser = Json.createParser(wikiStream())) { + testWiki(parser); + } + } + + public void testWikiInputStreamUTF16LE() throws Exception { + ByteArrayInputStream bin = new ByteArrayInputStream(wikiString() + .getBytes(StandardCharsets.UTF_16LE)); + try (JsonParser parser = Json.createParser(bin)) { + testWiki(parser); + } + } + + public void testWikiReader() throws Exception { + try (JsonParser parser = Json.createParser(wikiReader())) { + testWiki(parser); + } + } + + public void testWikiStructure() throws Exception { + try (JsonParser parser = Json.createParserFactory(null).createParser( + JsonBuilderTest.buildPerson())) { + testWiki(parser); + } + } + + static void testWiki(JsonParser parser) { + + Event event = parser.next(); + assertEquals(Event.START_OBJECT, event); + + testObjectStringValue(parser, "firstName", "John"); + testObjectStringValue(parser, "lastName", "Smith"); + + event = parser.next(); + assertEquals(Event.KEY_NAME, event); + assertEquals("age", parser.getString()); + + event = parser.next(); + assertEquals(Event.VALUE_NUMBER, event); + assertEquals(25, parser.getInt()); + assertEquals(25, parser.getLong()); + assertEquals(25, parser.getBigDecimal().intValue()); + assertTrue( parser.isIntegralNumber()); + + event = parser.next(); + assertEquals(Event.KEY_NAME, event); + assertEquals("address", parser.getString()); + + event = parser.next(); + assertEquals(Event.START_OBJECT, event); + + + testObjectStringValue(parser, "streetAddress", "21 2nd Street"); + testObjectStringValue(parser, "city", "New York"); + testObjectStringValue(parser, "state", "NY"); + testObjectStringValue(parser, "postalCode", "10021"); + + event = parser.next(); + assertEquals(Event.END_OBJECT, event); + + event = parser.next(); + assertEquals(Event.KEY_NAME, event); + assertEquals("phoneNumber", parser.getString()); + + event = parser.next(); + assertEquals(Event.START_ARRAY, event); + event = parser.next(); + assertEquals(Event.START_OBJECT, event); + testObjectStringValue(parser, "type", "home"); + testObjectStringValue(parser, "number", "212 555-1234"); + event = parser.next(); + assertEquals(Event.END_OBJECT, event); + + event = parser.next(); + assertEquals(Event.START_OBJECT, event); + testObjectStringValue(parser, "type", "fax"); + testObjectStringValue(parser, "number", "646 555-4567"); + event = parser.next(); + assertEquals(Event.END_OBJECT, event); + event = parser.next(); + assertEquals(Event.END_ARRAY, event); + + event = parser.next(); + assertEquals(Event.END_OBJECT, event); + } + + static void testObjectStringValue(JsonParser parser, String name, String value) { + Event event = parser.next(); + assertEquals(Event.KEY_NAME, event); + assertEquals(name, parser.getString()); + + event = parser.next(); + assertEquals(Event.VALUE_STRING, event); + assertEquals(value, parser.getString()); + } + + public void testNestedArrayReader() { + try (JsonParser parser = Json.createParser(new StringReader("[[],[[]]]"))) { + testNestedArray(parser); + } + } + + public void testNestedArrayStructure() { + try (JsonParser parser = Json.createParserFactory(null).createParser( + Json.createArrayBuilder() + .add(Json.createArrayBuilder()) + .add(Json.createArrayBuilder() + .add(Json.createArrayBuilder())) + .build())) { + testNestedArray(parser); + } + } + + static void testNestedArray(JsonParser parser) { + assertEquals(Event.START_ARRAY, parser.next()); + assertEquals(Event.START_ARRAY, parser.next()); + assertEquals(Event.END_ARRAY, parser.next()); + assertEquals(Event.START_ARRAY, parser.next()); + assertEquals(Event.START_ARRAY, parser.next()); + assertEquals(Event.END_ARRAY, parser.next()); + assertEquals(Event.END_ARRAY, parser.next()); + assertEquals(Event.END_ARRAY, parser.next()); + assertEquals(false, parser.hasNext()); + assertEquals(false, parser.hasNext()); + } + + public void testExceptionsReader() throws Exception { + try (JsonParser parser = Json.createParser(wikiReader())) { + testExceptions(parser); + } + } + + public void testExceptionsStructure() throws Exception { + try (JsonParser parser = Json.createParserFactory(null).createParser( + JsonBuilderTest.buildPerson())) { + testExceptions(parser); + } + } + + static void testExceptions(JsonParser parser) { + + Event event = parser.next(); + assertEquals(Event.START_OBJECT, event); + + try { + parser.getString(); + fail("JsonParser#getString() should have thrown exception in START_OBJECT state"); + } catch (IllegalStateException expected) { + // no-op + } + + try { + parser.isIntegralNumber(); + fail("JsonParser#getNumberType() should have thrown exception in START_OBJECT state"); + } catch (IllegalStateException expected) { + // no-op + } + + try { + parser.getInt(); + fail("JsonParser#getInt() should have thrown exception in START_OBJECT state"); + } catch (IllegalStateException expected) { + // no-op + } + + try { + parser.getLong(); + fail("JsonParser#getLong() should have thrown exception in START_OBJECT state"); + } catch (IllegalStateException expected) { + // no-op + } + + try { + parser.getBigDecimal(); + fail("JsonParser#getBigDecimal() should have thrown exception in START_OBJECT state"); + } catch (IllegalStateException expected) { + // no-op + } + } + + static String wikiString() { + String str; + try (Scanner scanner = new Scanner(wikiReader()) + .useDelimiter("\\A")) { + str = scanner.hasNext() ? scanner.next() : ""; + } + return str; + } + + static InputStream wikiStream() { + return JsonParserTest.class.getResourceAsStream("/wiki.json"); + } + + static Reader wikiReader() { + return new InputStreamReader( + JsonParserTest.class.getResourceAsStream("/wiki.json"), StandardCharsets.UTF_8); + } + + public void testIntNumber() { + JsonParserFactory factory = Json.createParserFactory(null); + + Random r = new Random(System.currentTimeMillis()); + + for(int i=0; i < 100000; i++) { + long num = i%2 == 0 ? r.nextInt() : r.nextLong(); + try (JsonParser parser = factory.createParser(new StringReader("["+num+"]"))) { + parser.next(); + parser.next(); + assertEquals("Fails for num="+num, new BigDecimal(num).intValue(), parser.getInt()); + } + } + + } + + public void testBigDecimalGetString() { + JsonParserFactory f = Json.createParserFactory(null); + JsonObject obj = Json.createObjectBuilder().add("a", BigDecimal.ONE).build(); + try (JsonParser parser = f.createParser(obj)) { + parser.next(); + parser.next(); + parser.next(); + assertEquals("Fails for BigDecimal=1", "1", parser.getString()); + } + } + + public void testIntGetString() { + JsonParserFactory f = Json.createParserFactory(null); + JsonObject obj = Json.createObjectBuilder().add("a", 5).build(); + try (JsonParser parser = f.createParser(obj)) { + parser.next(); + parser.next(); + parser.next(); + assertEquals("Fails for int=5", "5", parser.getString()); + } + } + static class MyBufferPool implements BufferPool { + private boolean takeCalled; + private boolean recycleCalled; + private final char[] buf; + + MyBufferPool(int size) { + buf = new char[size]; + } + + @Override + public char[] take() { + takeCalled = true; + return buf; + } + + @Override + public void recycle(char[] buf) { + recycleCalled = true; + } + + boolean isTakeCalled() { + return takeCalled; + } + + boolean isRecycleCalled() { + return recycleCalled; + } + } + + public void testBufferPoolFeature() { + final MyBufferPool bufferPool = new MyBufferPool(1024); + Map config = new HashMap() {{ + put(BufferPool.class.getName(), bufferPool); + }}; + + JsonParserFactory factory = Json.createParserFactory(config); + try (JsonParser parser = factory.createParser(new StringReader("[]"))) { + parser.next(); + parser.next(); + } + assertTrue(bufferPool.isTakeCalled()); + assertTrue(bufferPool.isRecycleCalled()); + } + + public void testBufferSizes() { + Random r = new Random(System.currentTimeMillis()); + for(int size=100; size < 1000; size++) { + final MyBufferPool bufferPool = new MyBufferPool(size); + Map config = new HashMap() {{ + put(BufferPool.class.getName(), bufferPool); + }}; + JsonParserFactory factory = Json.createParserFactory(config); + + StringBuilder sb = new StringBuilder(); + for(int i=0; i < 1000; i++) { + sb.append('a'); + String name = sb.toString(); + long num = i%2 == 0 ? r.nextInt() : r.nextLong(); + String str = "{\""+name+"\":["+num+"]}"; + try (JsonParser parser = factory.createParser(new StringReader(str))) { + parser.next(); + parser.next(); + assertEquals("Fails for " + str, name, parser.getString()); + parser.next(); + parser.next(); + assertEquals("Fails for "+str, new BigDecimal(num).intValue(), parser.getInt()); + } + } + } + } + + // Tests for string starting on buffer boundary (JSONP-15) + // xxxxxxx"xxxxxxxxx" + // ^ + // | + // 4096 + public void testStringUsingStandardBuffer() throws Throwable { + JsonParserFactory factory = Json.createParserFactory(null); + StringBuilder sb = new StringBuilder(); + for(int i=0; i < 40000; i++) { + sb.append('a'); + String name = sb.toString(); + String str = "{\""+name+"\":\""+name+"\"}"; + try (JsonParser parser = factory.createParser(new StringReader(str))) { + parser.next(); + parser.next(); + assertEquals("Fails for size=" + i, name, parser.getString()); + parser.next(); + assertEquals("Fails for size=" + i, name, parser.getString()); + } catch (Throwable e) { + throw new Throwable("Failed for size=" + i, e); + } + } + } + + // Tests for int starting on buffer boundary + // xxxxxxx"xxxxxxxxx" + // ^ + // | + // 4096 + public void testIntegerUsingStandardBuffer() throws Throwable { + Random r = new Random(System.currentTimeMillis()); + JsonParserFactory factory = Json.createParserFactory(null); + StringBuilder sb = new StringBuilder(); + for(int i=0; i < 40000; i++) { + sb.append('a'); + String name = sb.toString(); + int num = r.nextInt(); + String str = "{\"" + name + "\":" + num + "}"; + try (JsonParser parser = factory.createParser(new StringReader(str))) { + parser.next(); + parser.next(); + assertEquals("Fails for size=" + i, name, parser.getString()); + parser.next(); + assertEquals("Fails for size=" + i, num, parser.getInt()); + } catch (Throwable e) { + throw new Throwable("Failed for size=" + i, e); + } + } + } + + public void testStringUsingBuffers() throws Throwable { + for(int size=20; size < 500; size++) { + final MyBufferPool bufferPool = new MyBufferPool(size); + Map config = new HashMap() {{ + put(BufferPool.class.getName(), bufferPool); + }}; + JsonParserFactory factory = Json.createParserFactory(config); + + StringBuilder sb = new StringBuilder(); + for(int i=0; i < 1000; i++) { + sb.append('a'); + String name = sb.toString(); + String str = "{\""+name+"\":\""+name+"\"}"; + JsonLocation location; + try (JsonParser parser = factory.createParser(new StringReader(str))) { + parser.next(); + parser.next(); + assertEquals("name fails for buffer size=" + size + " name length=" + i, name, parser.getString()); + location = parser.getLocation(); + assertEquals("Stream offset fails for buffer size=" + size + " name length=" + i, + name.length() + 3, location.getStreamOffset()); + assertEquals("Column value fails for buffer size=" + size + " name length=" + i, + name.length() + 4, location.getColumnNumber()); + assertEquals("Line value fails for buffer size=" + size + " name length=" + i, + 1, location.getLineNumber()); + + parser.next(); + assertEquals("value fails for buffer size=" + size + " name length=" + i, name, parser.getString()); + location = parser.getLocation(); + assertEquals("Stream offset fails for buffer size=" + size + " name length=" + i, 2 * name.length() + 6, location.getStreamOffset()); + assertEquals("Column value fails for buffer size=" + size + " name length=" + i, + 2 * name.length() + 7, location.getColumnNumber()); + assertEquals("Line value fails for buffer size=" + size + " name length=" + i, + 1, location.getLineNumber()); + } catch (Throwable e) { + throw new Throwable("Failed for buffer size=" + size + " name length=" + i, e); + } + } + } + } + +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonParsingExceptionTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonParsingExceptionTest.java new file mode 100644 index 00000000..be724bbd --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonParsingExceptionTest.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import junit.framework.TestCase; + +import javax.json.Json; +import javax.json.stream.JsonLocation; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParsingException; +import java.io.StringReader; + +/** + * JsonParsingException Tests + * + * @author Jitendra Kotamraju + */ +public class JsonParsingExceptionTest extends TestCase { + + public void testWrongJson() { + // testMalformedJson("", null); Allowed in 1.1 + } + + public void testWrongJson1() { + // testMalformedJson("{}{}", null); Allowed in 1.1 + } + + public void testWrongJson2() { + // testMalformedJson("{", null); Allowed in 1.1 + } + + public void testWrongJson3() { + testMalformedJson("{[]", null); + } + + public void testWrongJson4() { + testMalformedJson("{]", null); + } + + public void testWrongJson5() { + testMalformedJson("{\"a\":[]]", null); + } + + public void testWrongJson6() { + testMalformedJson("[ {}, [] }", null); + } + + public void testWrongJson61() { + testMalformedJson("[ {}, {} }", null); + } + + public void testWrongJson7() { + testMalformedJson("{ \"a\" : {}, \"b\": {} ]", null); + } + + public void testWrongJson8() { + testMalformedJson("{ \"a\" : {}, \"b\": [] ]", null); + } + + public void testWrongUnicode() { + testMalformedJson("[ \"\\uX00F\" ]", null); + testMalformedJson("[ \"\\u000Z\" ]", null); + testMalformedJson("[ \"\\u000\" ]", null); + testMalformedJson("[ \"\\u00\" ]", null); + testMalformedJson("[ \"\\u0\" ]", null); + testMalformedJson("[ \"\\u\" ]", null); + testMalformedJson("[ \"\\u\"", null); + testMalformedJson("[ \"\\", null); + } + + public void testControlChar() { + testMalformedJson("[ \"\u0000\" ]", null); + testMalformedJson("[ \"\u000c\" ]", null); + testMalformedJson("[ \"\u000f\" ]", null); + testMalformedJson("[ \"\u001F\" ]", null); + testMalformedJson("[ \"\u001f\" ]", null); + } + + public void testLocation1() { + testMalformedJson("x", new MyLocation(1, 1, 0)); + testMalformedJson("{]", new MyLocation(1, 2, 1)); + testMalformedJson("[}", new MyLocation(1, 2, 1)); + testMalformedJson("[a", new MyLocation(1, 2, 1)); + testMalformedJson("[nuLl]", new MyLocation(1, 4, 3)); + testMalformedJson("[falsE]", new MyLocation(1, 6, 5)); + // testMalformedJson("[][]", new MyLocation(1, 3, 2)); allowed in 1.1 + testMalformedJson("[1234L]", new MyLocation(1, 6, 5)); + } + + public void testLocation2() { + testMalformedJson("[null\n}", new MyLocation(2, 1, 6)); + testMalformedJson("[null\r\n}", new MyLocation(2, 1, 7)); + testMalformedJson("[null\n, null\n}", new MyLocation(3, 1, 13)); + testMalformedJson("[null\r\n, null\r\n}", new MyLocation(3, 1, 15)); + } + + private void testMalformedJson(String json, JsonLocation expected) { + try (JsonParser parser = Json.createParser(new StringReader(json))) { + while (parser.hasNext()) { + parser.next(); + } + fail("Expected to throw JsonParsingException for " + json); + } catch (JsonParsingException je) { + // Expected + if (expected != null) { + JsonLocation got = je.getLocation(); + assertEquals(expected.getLineNumber(), got.getLineNumber()); + assertEquals(expected.getColumnNumber(), got.getColumnNumber()); + assertEquals(expected.getStreamOffset(), got.getStreamOffset()); + } + } + } + + private static class MyLocation implements JsonLocation { + private final long columnNo; + private final long lineNo; + private final long streamOffset; + + MyLocation(long lineNo, long columnNo, long streamOffset) { + this.lineNo = lineNo; + this.columnNo = columnNo; + this.streamOffset = streamOffset; + } + + @Override + public long getLineNumber() { + return lineNo; + } + + @Override + public long getColumnNumber() { + return columnNo; + } + + @Override + public long getStreamOffset() { + return streamOffset; + } + } + +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonPatchBugsTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonPatchBugsTest.java new file mode 100644 index 00000000..6267b844 --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonPatchBugsTest.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonException; +import javax.json.JsonPatch; +import org.junit.Test; + +/** + * + * @author lukas + */ +public class JsonPatchBugsTest { + + // https://github.com/javaee/jsonp/issues/58 + @Test(expected = JsonException.class) + public void applyThrowsJsonException() { + JsonArray array = Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("name", "Bob") + .build()) + .build(); + JsonPatch patch = Json.createPatchBuilder() + .replace("/0/name", "Bobek") + .replace("/1/name", "Vila Amalka") + .build(); + JsonArray result = patch.apply(array); + } +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonPatchBuilderTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonPatchBuilderTest.java new file mode 100644 index 00000000..d37d5bbd --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonPatchBuilderTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import javax.json.Json; +import javax.json.JsonObject; +import javax.json.JsonPatchBuilder; + +import org.junit.Test; + +/** + * + * @author Alex Soto + * + */ +public class JsonPatchBuilderTest { + + @Test + public void shouldBuildJsonPatchExpressionUsingJsonPatchBuilder() { + JsonPatchBuilder patchBuilder = Json.createPatchBuilder(); + JsonObject result = patchBuilder.add("/email", "john@example.com") + .replace("/age", 30) + .remove("/phoneNumber") + .test("/firstName", "John") + .copy("/address/lastName", "/lastName") + .build() + .apply(buildPerson()); + assertThat(result, is(expectedBuildPerson())); + + } + + static JsonObject expectedBuildPerson() { + return Json.createObjectBuilder() + .add("firstName", "John") + .add("lastName", "Smith") + .add("email", "john@example.com") + .add("age", 30) + .add("address", Json.createObjectBuilder() + .add("lastName", "Smith") + .add("streetAddress", "21 2nd Street") + .add("city", "New York") + .add("state", "NY") + .add("postalCode", "10021")) + .build(); + } + + static JsonObject buildPerson() { + return Json.createObjectBuilder() + .add("firstName", "John") + .add("lastName", "Smith") + .add("age", 25) + .add("address", Json.createObjectBuilder() + .add("streetAddress", "21 2nd Street") + .add("city", "New York") + .add("state", "NY") + .add("postalCode", "10021")) + .add("phoneNumber", Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("type", "home") + .add("number", "212 555-1234")) + .add(Json.createObjectBuilder() + .add("type", "fax") + .add("number", "646 555-4567"))) + .build(); + } +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonPatchDiffTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonPatchDiffTest.java new file mode 100644 index 00000000..cc95ec5b --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonPatchDiffTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.json.JsonPatch; +import javax.json.JsonReader; +import javax.json.JsonString; +import javax.json.JsonStructure; +import javax.json.JsonValue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +/** + * + * @author Alex Soto + * + */ +@RunWith(Parameterized.class) +public class JsonPatchDiffTest { + + @Parameters(name = "{index}: ({0})={1}") + public static Iterable data() throws Exception { + List examples = new ArrayList(); + JsonArray data = loadData(); + for (JsonValue jsonValue : data) { + JsonObject test = (JsonObject) jsonValue; + Object[] testData = new Object[4]; + testData[0] = test.get("original"); + testData[1] = test.get("target"); + testData[2] = test.get("expected"); + testData[3] = createExceptionClass((JsonString)test.get("exception")); + + examples.add(testData); + } + + return examples; + } + + private static Class createExceptionClass( + JsonString exceptionClassName) throws ClassNotFoundException { + if (exceptionClassName != null) { + return (Class) Class + .forName(exceptionClassName.getString()); + } + return null; + } + + private static JsonArray loadData() { + InputStream testData = JsonPatchTest.class + .getResourceAsStream("/jsonpatchdiff.json"); + JsonReader reader = Json.createReader(testData); + JsonArray data = (JsonArray) reader.read(); + return data; + } + + private JsonStructure original; + private JsonStructure target; + private JsonValue expected; + private Class expectedException; + + public JsonPatchDiffTest(JsonStructure original, JsonStructure target, + JsonValue expected, Class expectedException) { + super(); + this.original = original; + this.target = target; + this.expected = expected; + this.expectedException = expectedException; + } + + @Test + public void shouldExecuteJsonPatchDiffOperationsToJsonDocument() { + try { + JsonPatch diff = Json.createDiff(this.original, this.target); + assertThat(diff, is(Json.createPatchBuilder((JsonArray) expected).build())); + assertThat(expectedException, nullValue()); + } catch (Exception e) { + if (expectedException == null) { + fail(e.getMessage()); + } else { + assertThat(e, instanceOf(expectedException)); + } + } + } +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonPatchOperationTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonPatchOperationTest.java new file mode 100644 index 00000000..f5231184 --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonPatchOperationTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import javax.json.JsonException; +import javax.json.JsonPatch.Operation; +import org.junit.Assert; +import org.junit.Test; + +/** + * + * @author lukas + */ +public class JsonPatchOperationTest { + + private static final String[] opNames = {"add", "remove", "replace", "move", "copy", "test"}; + + @Test + public void fromOperationName() { + for (String op: opNames) { + Assert.assertEquals(Operation.valueOf(op.toUpperCase()), Operation.fromOperationName(op)); + } + for (String op: opNames) { + Assert.assertEquals(Operation.valueOf(op.toUpperCase()), Operation.fromOperationName(op.toUpperCase())); + } + } + + @Test(expected = JsonException.class) + public void fromInvalidOperationName() { + Operation.fromOperationName("undef"); + } +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonPatchTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonPatchTest.java new file mode 100644 index 00000000..daeca411 --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonPatchTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.json.JsonPatch; +import javax.json.JsonReader; +import javax.json.JsonString; +import javax.json.JsonStructure; +import javax.json.JsonValue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +/** + * + * @author Alex Soto + * + */ +@RunWith(Parameterized.class) +public class JsonPatchTest { + + @Parameters(name = "{index}: ({0})={1}") + public static Iterable data() throws Exception { + List examples = new ArrayList<>(); + JsonArray data = loadData(); + for (JsonValue jsonValue : data) { + JsonObject test = (JsonObject) jsonValue; + Object[] testData = new Object[4]; + testData[0] = createPatchArray(test.get("op")); + testData[1] = test.get("target"); + testData[2] = test.get("expected"); + testData[3] = createExceptionClass((JsonString)test.get("exception")); + + examples.add(testData); + } + + return examples; + } + + private static Class createExceptionClass( + JsonString exceptionClassName) throws ClassNotFoundException { + if (exceptionClassName != null) { + return (Class) Class + .forName(exceptionClassName.getString()); + } + return null; + } + + private static JsonArray createPatchArray(JsonValue object) { + return Json.createArrayBuilder().add(object).build(); + } + + private static JsonArray loadData() { + InputStream testData = JsonPatchTest.class + .getResourceAsStream("/jsonpatch.json"); + JsonReader reader = Json.createReader(testData); + return (JsonArray) reader.read(); + } + + private JsonArray patch; + private JsonStructure target; + private JsonValue expected; + private Class expectedException; + + public JsonPatchTest(JsonArray patch, JsonStructure target, + JsonValue expected, Class expectedException) { + super(); + this.patch = patch; + this.target = target; + this.expected = expected; + this.expectedException = expectedException; + } + + @Test + public void shouldExecuteJsonPatchOperationsToJsonDocument() { + try { + JsonPatch patch = Json.createPatchBuilder(this.patch).build(); + JsonStructure output = patch.apply(target); + assertThat(output, is(expected)); + assertThat(expectedException, nullValue()); + } catch (Exception e) { + if (expectedException == null) { + fail(e.getMessage()); + } else { + assertThat(e, instanceOf(expectedException)); + } + } + } +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonPointerAddOperationTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonPointerAddOperationTest.java new file mode 100644 index 00000000..41a92e6b --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonPointerAddOperationTest.java @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.util.Arrays; + +import javax.json.Json; +import javax.json.JsonObject; +import javax.json.JsonPointer; +import javax.json.JsonStructure; +import javax.json.JsonValue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +/** + * + * @author Alex Soto + * + */ +@RunWith(Parameterized.class) +public class JsonPointerAddOperationTest { + + @Parameters(name = "{index}: ({0})={1}") + public static Iterable data() throws Exception { + return Arrays.asList(new Object[][] { + {buildSimpleAddPatch(), buildAddress(), buildExpectedAddress() }, + {buildComplexAddPatch(), buildPerson(), buildExpectedPerson()}, + {buildArrayAddPatchInPosition(), buildPerson(), buildExpectedPersonConcreteArrayPosition()}, + {buildArrayAddPatchInLastPosition(), buildPerson(), buildExpectedPersonArrayLastPosition()} + }); + } + + private JsonObject pathOperation; + private JsonStructure target; + private JsonValue expectedResult; + + public JsonPointerAddOperationTest(JsonObject pathOperation, + JsonStructure target, JsonValue expectedResult) { + super(); + this.pathOperation = pathOperation; + this.target = target; + this.expectedResult = expectedResult; + } + + @Test + public void shouldAddElementsToExistingJsonDocument() { + JsonPointer pointer = Json.createPointer(pathOperation.getString("path")); + JsonObject modified = (JsonObject) pointer.add(target, pathOperation.get("value")); + assertThat(modified, is(expectedResult)); + } + + static JsonObject buildAddress() { + return Json.createObjectBuilder() + .add("streetAddress", "21 2nd Street") + .add("city", "New York") + .add("state", "NY") + .add("postalCode", "10021") + .build(); + } + static JsonObject buildComplexAddPatch() { + return Json.createObjectBuilder() + .add("op", "add") + .add("path", "/address/streetAddress") + .add("value", "myaddress") + .build(); + } + static JsonObject buildSimpleAddPatch() { + return Json.createObjectBuilder() + .add("op", "add") + .add("path", "/streetAddress") + .add("value", "myaddress") + .build(); + } + static JsonObject buildArrayAddPatchInPosition() { + return Json.createObjectBuilder() + .add("op", "add") + .add("path", "/phoneNumber/0") + .add("value", Json.createObjectBuilder() + .add("type", "home") + .add("number", "200 555-1234")) + .build(); + } + static JsonObject buildArrayAddPatchInLastPosition() { + return Json.createObjectBuilder() + .add("op", "add") + .add("path", "/phoneNumber/-") + .add("value", Json.createObjectBuilder() + .add("type", "home") + .add("number", "200 555-1234")) + .build(); + } + static JsonObject buildExpectedAddress() { + return Json.createObjectBuilder() + .add("streetAddress", "myaddress") + .add("city", "New York") + .add("state", "NY") + .add("postalCode", "10021") + .build(); + } + static JsonObject buildPerson() { + return Json.createObjectBuilder() + .add("firstName", "John") + .add("lastName", "Smith") + .add("age", 25) + .add("address", Json.createObjectBuilder() + .add("streetAddress", "21 2nd Street") + .add("city", "New York") + .add("state", "NY") + .add("postalCode", "10021")) + .add("phoneNumber", Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("type", "home") + .add("number", "212 555-1234")) + .add(Json.createObjectBuilder() + .add("type", "fax") + .add("number", "646 555-4567"))) + .build(); + } + static JsonObject buildExpectedPersonConcreteArrayPosition() { + return Json.createObjectBuilder() + .add("firstName", "John") + .add("lastName", "Smith") + .add("age", 25) + .add("address", Json.createObjectBuilder() + .add("streetAddress", "21 2nd Street") + .add("city", "New York") + .add("state", "NY") + .add("postalCode", "10021")) + .add("phoneNumber", Json.createArrayBuilder() + .add((Json.createObjectBuilder() + .add("type", "home") + .add("number", "200 555-1234"))) + .add(Json.createObjectBuilder() + .add("type", "home") + .add("number", "212 555-1234")) + .add(Json.createObjectBuilder() + .add("type", "fax") + .add("number", "646 555-4567"))) + .build(); + } + static JsonObject buildExpectedPersonArrayLastPosition() { + return Json.createObjectBuilder() + .add("firstName", "John") + .add("lastName", "Smith") + .add("age", 25) + .add("address", Json.createObjectBuilder() + .add("streetAddress", "21 2nd Street") + .add("city", "New York") + .add("state", "NY") + .add("postalCode", "10021")) + .add("phoneNumber", Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("type", "home") + .add("number", "212 555-1234")) + .add(Json.createObjectBuilder() + .add("type", "fax") + .add("number", "646 555-4567")) + .add(Json.createObjectBuilder() + .add("type", "home") + .add("number", "200 555-1234"))) + .build(); + } + static JsonObject buildExpectedPerson() { + return Json.createObjectBuilder() + .add("firstName", "John") + .add("lastName", "Smith") + .add("age", 25) + .add("address", Json.createObjectBuilder() + .add("streetAddress", "myaddress") + .add("city", "New York") + .add("state", "NY") + .add("postalCode", "10021")) + .add("phoneNumber", Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("type", "home") + .add("number", "212 555-1234")) + .add(Json.createObjectBuilder() + .add("type", "fax") + .add("number", "646 555-4567"))) + .build(); + } + +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonPointerEscapeTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonPointerEscapeTest.java new file mode 100644 index 00000000..cd9a8d82 --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonPointerEscapeTest.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import org.junit.Test; + +import javax.json.Json; + +import static org.junit.Assert.assertEquals; + +/** + * JSON pointer escape/unescape tests. + * + * @author Dmitry Kornilov + */ +public class JsonPointerEscapeTest { + + @Test + public void escapeTest() { + assertEquals("a~1b", Json.encodePointer("a/b")); + assertEquals("a~0b~1c", Json.encodePointer("a~b/c")); + } + + @Test + public void unescapeTest() { + assertEquals("/a/b", Json.decodePointer("/a~1b")); + assertEquals("/~1", Json.decodePointer("/~01")); + } +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonPointerRemoveOperationTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonPointerRemoveOperationTest.java new file mode 100644 index 00000000..bc9abdf9 --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonPointerRemoveOperationTest.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.util.Arrays; + +import javax.json.Json; +import javax.json.JsonObject; +import javax.json.JsonPointer; +import javax.json.JsonStructure; +import javax.json.JsonValue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +/** + * + * @author Alex Soto + * + */ +@RunWith(Parameterized.class) +public class JsonPointerRemoveOperationTest { + + @Parameters(name = "{index}: ({0})={1}") + public static Iterable data() throws Exception { + return Arrays.asList(new Object[][] { + {buildSimpleRemovePatch(), buildAddress(), buildExpectedRemovedAddress() }, + {buildComplexRemovePatch(), buildPerson(), buildExpectedPersonWithoutStreetAddress()}, + {buildArrayRemovePatchInPosition(), buildPerson(), buildPersonWithoutFirstPhone()} + }); + } + + private JsonObject pathOperation; + private JsonStructure target; + private JsonValue expectedResult; + + public JsonPointerRemoveOperationTest(JsonObject pathOperation, + JsonObject target, JsonValue expectedResult) { + super(); + this.pathOperation = pathOperation; + this.target = target; + this.expectedResult = expectedResult; + } + + @Test + public void shouldRemoveElementsToExistingJsonDocument() { + JsonPointer pointer = Json.createPointer(pathOperation.getString("path")); + JsonObject modified = (JsonObject) pointer.remove(target); + assertThat(modified, is(expectedResult)); + } + + static JsonObject buildPerson() { + return Json.createObjectBuilder() + .add("firstName", "John") + .add("lastName", "Smith") + .add("age", 25) + .add("address", Json.createObjectBuilder() + .add("streetAddress", "21 2nd Street") + .add("city", "New York") + .add("state", "NY") + .add("postalCode", "10021")) + .add("phoneNumber", Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("type", "home") + .add("number", "212 555-1234")) + .add(Json.createObjectBuilder() + .add("type", "fax") + .add("number", "646 555-4567"))) + .build(); + } + static JsonObject buildAddress() { + return Json.createObjectBuilder() + .add("streetAddress", "21 2nd Street") + .add("city", "New York") + .add("state", "NY") + .add("postalCode", "10021") + .build(); + } + static JsonObject buildComplexRemovePatch() { + return Json.createObjectBuilder() + .add("op", "remove") + .add("path", "/address/streetAddress") + .build(); + } + static JsonObject buildSimpleRemovePatch() { + return Json.createObjectBuilder() + .add("op", "remove") + .add("path", "/streetAddress") + .build(); + } + static JsonObject buildArrayRemovePatchInPosition() { + return Json.createObjectBuilder() + .add("op", "remove") + .add("path", "/phoneNumber/0") + .build(); + } + static JsonObject buildExpectedRemovedAddress() { + return Json.createObjectBuilder() + .add("city", "New York") + .add("state", "NY") + .add("postalCode", "10021") + .build(); + } + static JsonObject buildPersonWithoutFirstPhone() { + return Json.createObjectBuilder() + .add("firstName", "John") + .add("lastName", "Smith") + .add("age", 25) + .add("address", Json.createObjectBuilder() + .add("streetAddress", "21 2nd Street") + .add("city", "New York") + .add("state", "NY") + .add("postalCode", "10021")) + .add("phoneNumber", Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("type", "fax") + .add("number", "646 555-4567"))) + .build(); + } + static JsonObject buildExpectedPersonWithoutStreetAddress() { + return Json.createObjectBuilder() + .add("firstName", "John") + .add("lastName", "Smith") + .add("age", 25) + .add("address", Json.createObjectBuilder() + .add("city", "New York") + .add("state", "NY") + .add("postalCode", "10021")) + .add("phoneNumber", Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("type", "home") + .add("number", "212 555-1234")) + .add(Json.createObjectBuilder() + .add("type", "fax") + .add("number", "646 555-4567"))) + .build(); + } +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonPointerReplaceOperationTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonPointerReplaceOperationTest.java new file mode 100644 index 00000000..cc420029 --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonPointerReplaceOperationTest.java @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +import java.util.Arrays; + +import javax.json.Json; +import javax.json.JsonException; +import javax.json.JsonObject; +import javax.json.JsonPointer; +import javax.json.JsonStructure; +import javax.json.JsonValue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +/** + * + * @author Alex Soto + * + */ +@RunWith(Parameterized.class) +public class JsonPointerReplaceOperationTest { + + @Parameters(name = "{index}: ({0})={1}") + public static Iterable data() throws Exception { + return Arrays.asList(new Object[][] { + {buildSimpleReplacePatch(), buildAddress(), buildExpectedAddress(), null}, + {buildComplexReplacePatch(), buildPerson(), buildExpectedPerson(), null}, + {buildArrayReplacePatchInPosition(), buildPerson(), buildExpectedPersonConcreteArrayPosition(), null}, + {buildArrayAddPatchInLastPosition(), buildPerson(), null, JsonException.class}, + {buildNoneExistingReplacePatch(), buildAddress(), null, JsonException.class} + }); + } + + private JsonObject pathOperation; + private JsonStructure target; + private JsonValue expectedResult; + private Class expectedException; + + public JsonPointerReplaceOperationTest(JsonObject pathOperation, + JsonStructure target, JsonValue expectedResult, Class expectedException) { + super(); + this.pathOperation = pathOperation; + this.target = target; + this.expectedResult = expectedResult; + this.expectedException = expectedException; + } + + @Test + public void shouldReplaceElementsToExistingJsonDocument() { + try { + JsonPointer pointer = Json.createPointer(pathOperation.getString("path")); + JsonObject modified = (JsonObject) pointer.replace(target, pathOperation.get("value")); + assertThat(modified, is(expectedResult)); + assertThat(expectedException, nullValue()); + } catch(Exception e) { + if(expectedException == null) { + fail(e.getMessage()); + } else { + assertThat(e, instanceOf(expectedException)); + } + } + } + + static JsonObject buildAddress() { + return Json.createObjectBuilder() + .add("streetAddress", "21 2nd Street") + .add("city", "New York") + .add("state", "NY") + .add("postalCode", "10021") + .build(); + } + static JsonObject buildComplexReplacePatch() { + return Json.createObjectBuilder() + .add("op", "add") + .add("path", "/address/streetAddress") + .add("value", "myaddress") + .build(); + } + static JsonObject buildSimpleReplacePatch() { + return Json.createObjectBuilder() + .add("op", "replace") + .add("path", "/streetAddress") + .add("value", "myaddress") + .build(); + } + static JsonObject buildNoneExistingReplacePatch() { + return Json.createObjectBuilder() + .add("op", "replace") + .add("path", "/notexists") + .add("value", "myaddress") + .build(); + } + static JsonObject buildArrayReplacePatchInPosition() { + return Json.createObjectBuilder() + .add("op", "replace") + .add("path", "/phoneNumber/0") + .add("value", Json.createObjectBuilder() + .add("type", "home") + .add("number", "200 555-1234")) + .build(); + } + static JsonObject buildArrayAddPatchInLastPosition() { + return Json.createObjectBuilder() + .add("op", "add") + .add("path", "/phoneNumber/-") + .add("value", Json.createObjectBuilder() + .add("type", "home") + .add("number", "200 555-1234")) + .build(); + } + static JsonObject buildExpectedAddress() { + return Json.createObjectBuilder() + .add("streetAddress", "myaddress") + .add("city", "New York") + .add("state", "NY") + .add("postalCode", "10021") + .build(); + } + static JsonObject buildPerson() { + return Json.createObjectBuilder() + .add("firstName", "John") + .add("lastName", "Smith") + .add("age", 25) + .add("address", Json.createObjectBuilder() + .add("streetAddress", "21 2nd Street") + .add("city", "New York") + .add("state", "NY") + .add("postalCode", "10021")) + .add("phoneNumber", Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("type", "home") + .add("number", "212 555-1234")) + .add(Json.createObjectBuilder() + .add("type", "fax") + .add("number", "646 555-4567"))) + .build(); + } + static JsonObject buildExpectedPersonConcreteArrayPosition() { + return Json.createObjectBuilder() + .add("firstName", "John") + .add("lastName", "Smith") + .add("age", 25) + .add("address", Json.createObjectBuilder() + .add("streetAddress", "21 2nd Street") + .add("city", "New York") + .add("state", "NY") + .add("postalCode", "10021")) + .add("phoneNumber", Json.createArrayBuilder() + .add((Json.createObjectBuilder() + .add("type", "home") + .add("number", "200 555-1234"))) + .add(Json.createObjectBuilder() + .add("type", "fax") + .add("number", "646 555-4567"))) + .build(); + } + static JsonObject buildExpectedPerson() { + return Json.createObjectBuilder() + .add("firstName", "John") + .add("lastName", "Smith") + .add("age", 25) + .add("address", Json.createObjectBuilder() + .add("streetAddress", "myaddress") + .add("city", "New York") + .add("state", "NY") + .add("postalCode", "10021")) + .add("phoneNumber", Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("type", "home") + .add("number", "212 555-1234")) + .add(Json.createObjectBuilder() + .add("type", "fax") + .add("number", "646 555-4567"))) + .build(); + } + +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonPointerTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonPointerTest.java new file mode 100644 index 00000000..40459e4b --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonPointerTest.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.CoreMatchers.instanceOf; + +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.Arrays; + +import javax.json.Json; +import javax.json.JsonException; +import javax.json.JsonObject; +import javax.json.JsonPointer; +import javax.json.JsonReader; +import javax.json.JsonValue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +/** + * + * @author Alex Soto + * + */ +@RunWith(Parameterized.class) +public class JsonPointerTest { + + private static JsonObject rfc6901Example; + + @Parameters(name = "{index}: ({0})={1}") + public static Iterable data() throws Exception { + rfc6901Example = JsonPointerTest.readRfc6901Example(); + return Arrays.asList(new Object[][] { + {Json.createPointer(""), rfc6901Example, null }, + {Json.createPointer("/foo"), rfc6901Example.getJsonArray("foo"), null}, + {Json.createPointer("/foo/0"), rfc6901Example.getJsonArray("foo").get(0), null}, + {Json.createPointer("/foo/5"), null, JsonException.class}, + {Json.createPointer("/p/1"), null, JsonException.class}, + {Json.createPointer("/"), rfc6901Example.getJsonNumber(""), null}, + {Json.createPointer("/a~1b"), rfc6901Example.getJsonNumber("a/b"), null}, + {Json.createPointer("/m~0n"), rfc6901Example.getJsonNumber("m~n"), null}, + {Json.createPointer("/c%d"), rfc6901Example.getJsonNumber("c%d"), null}, + {Json.createPointer("/e^f"), rfc6901Example.getJsonNumber("e^f"), null}, + {Json.createPointer("/g|h"), rfc6901Example.getJsonNumber("g|h"), null}, + {Json.createPointer("/i\\j"), rfc6901Example.getJsonNumber("i\\j"), null}, + {Json.createPointer("/k\"l"), rfc6901Example.getJsonNumber("k\"l"), null}, + {Json.createPointer("/ "), rfc6901Example.getJsonNumber(" "), null}, + {Json.createPointer("/notexists"), null, JsonException.class}, + {Json.createPointer("/s/t"), null, JsonException.class}, + {Json.createPointer("/o"), JsonObject.NULL, null} + }); + } + + private JsonPointer pointer; + private JsonValue expected; + private Class expectedException; + + public JsonPointerTest(JsonPointer pointer, JsonValue expected, Class expectedException) { + super(); + this.pointer = pointer; + this.expected = expected; + this.expectedException = expectedException; + } + + @Test + public void shouldEvaluateJsonPointerExpressions() { + try { + JsonValue result = pointer.getValue(rfc6901Example); + assertThat(result, is(expected)); + assertThat(expectedException, nullValue()); + } catch(Exception e) { + if(expectedException == null) { + fail(e.getMessage()); + } else { + assertThat(e, instanceOf(expectedException)); + } + } + } + + static JsonObject readRfc6901Example() throws Exception { + Reader rfc6901Reader = new InputStreamReader(JsonReaderTest.class.getResourceAsStream("/rfc6901.json")); + JsonReader reader = Json.createReader(rfc6901Reader); + JsonValue value = reader.readObject(); + reader.close(); + return (JsonObject) value; + } +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonReaderTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonReaderTest.java new file mode 100644 index 00000000..7a245fed --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonReaderTest.java @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.HashMap; +import java.util.Map; + +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonNumber; +import javax.json.JsonObject; +import javax.json.JsonReader; +import javax.json.JsonReaderFactory; +import javax.json.JsonValue; + +import org.glassfish.json.api.BufferPool; + +import junit.framework.TestCase; + +/** + * @author Jitendra Kotamraju + */ +public class JsonReaderTest extends TestCase { + public JsonReaderTest(String testName) { + super(testName); + } + + public void testObject() throws Exception { + JsonObject person = readPerson(); + JsonObjectTest.testPerson(person); + } + + public void testEscapedString() throws Exception { + // u00ff is escaped once, not escaped once + JsonReader reader = Json.createReader(new StringReader("[\"\\u0000\\u00ff\u00ff\"]")); + JsonArray array = reader.readArray(); + reader.close(); + String str = array.getString(0); + assertEquals("\u0000\u00ff\u00ff", str); + } + + public void testPrimitiveIntNumbers() { + String[] borderlineCases = new String[]{ + "214748364", + Integer.toString(Integer.MAX_VALUE), + Long.toString(Integer.MAX_VALUE + 1L), + "-214748364", + Integer.toString(Integer.MIN_VALUE), + Long.toString(Integer.MIN_VALUE - 1L) + }; + for (String num : borderlineCases) { + JsonReader reader = Json.createReader(new StringReader("["+num+"]")); + try { + JsonArray array = reader.readArray(); + JsonNumber value = (JsonNumber) array.get(0); + assertEquals("Fails for num="+num, new BigInteger(num).longValue(), value.longValue()); + } finally { + reader.close(); + } + } + } + + public void testPrimitiveLongNumbers() { + String[] borderlineCases = new String[]{ + "922337203685477580", + Long.toString(Long.MAX_VALUE), + new BigInteger(Long.toString(Long.MAX_VALUE)).add(BigInteger.ONE).toString(), + "-922337203685477580", + Long.toString(Long.MIN_VALUE), + new BigInteger(Long.toString(Long.MIN_VALUE)).subtract(BigInteger.ONE).toString() + }; + for (String num : borderlineCases) { + JsonReader reader = Json.createReader(new StringReader("["+num+"]")); + try { + JsonArray array = reader.readArray(); + JsonNumber value = (JsonNumber) array.get(0); + assertEquals("Fails for num="+num, new BigInteger(num), value.bigIntegerValueExact()); + } finally { + reader.close(); + } + } + } + + public void testUnknownFeature() throws Exception { + Map config = new HashMap<>(); + config.put("foo", true); + JsonReaderFactory factory = Json.createReaderFactory(config); + factory.createReader(new StringReader("{}")); + Map config1 = factory.getConfigInUse(); + if (config1.size() > 0) { + fail("Shouldn't have any config in use"); + } + } + + public void testIllegalStateExcepton() throws Exception { + JsonReader reader = Json.createReader(new StringReader("{}")); + reader.readObject(); + try { + reader.readObject(); + } catch (IllegalStateException expected) { + // no-op + } + reader.close(); + + reader = Json.createReader(new StringReader("[]")); + reader.readArray(); + try { + reader.readArray(); + } catch (IllegalStateException expected) { + // no-op + } + reader.close(); + + reader = Json.createReader(new StringReader("{}")); + reader.read(); + try { + reader.read(); + } catch (IllegalStateException expected) { + // no-op + } + reader.close(); + } + + static JsonObject readPerson() throws Exception { + Reader wikiReader = new InputStreamReader(JsonReaderTest.class.getResourceAsStream("/wiki.json")); + JsonReader reader = Json.createReader(wikiReader); + JsonValue value = reader.readObject(); + reader.close(); + return (JsonObject) value; + } + + // JSONP-23 cached empty string is not reset + public void testEmptyStringUsingStandardBuffer() throws Throwable { + JsonReaderFactory factory = Json.createReaderFactory(null); + StringBuilder sb = new StringBuilder(); + for(int i=0; i < 40000; i++) { + sb.append('a'); + String name = sb.toString(); + String str = "[1, \"\", \""+name+"\", \"\", \""+name+"\", \"\", 100]"; + try { + JsonReader reader = factory.createReader(new StringReader(str)); + JsonArray array = reader.readArray(); + assertEquals(1, array.getInt(0)); + assertEquals("", array.getString(1)); + assertEquals(name, array.getString(2)); + assertEquals("", array.getString(3)); + assertEquals(name, array.getString(4)); + assertEquals("", array.getString(5)); + assertEquals(100, array.getInt(6)); + reader.close(); + } catch (Throwable t) { + throw new Throwable("Failed for name length="+i, t); + } + } + } + + // JSONP-23 cached empty string is not reset + public void testEmptyStringUsingBuffers() throws Throwable { + for(int size=20; size < 500; size++) { + final JsonParserTest.MyBufferPool bufferPool = new JsonParserTest.MyBufferPool(size); + Map config = new HashMap() {{ + put(BufferPool.class.getName(), bufferPool); + }}; + JsonReaderFactory factory = Json.createReaderFactory(config); + + StringBuilder sb = new StringBuilder(); + for(int i=0; i < 1000; i++) { + sb.append('a'); + String name = sb.toString(); + String str = "[1, \"\", \""+name+"\", \"\", \""+name+"\", \"\", 100]"; + try { + JsonReader reader = factory.createReader(new StringReader(str)); + JsonArray array = reader.readArray(); + assertEquals(1, array.getInt(0)); + assertEquals("", array.getString(1)); + assertEquals(name, array.getString(2)); + assertEquals("", array.getString(3)); + assertEquals(name, array.getString(4)); + assertEquals("", array.getString(5)); + assertEquals(100, array.getInt(6)); + reader.close(); + } catch (Throwable t) { + throw new Throwable("Failed for buffer size="+size+" name length="+i, t); + } + } + } + } + +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonSamplesParsingTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonSamplesParsingTest.java new file mode 100644 index 00000000..a8456887 --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonSamplesParsingTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import junit.framework.TestCase; + +import javax.json.Json; +import javax.json.JsonException; +import javax.json.stream.JsonParser; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; + +/** + * JsonParser tests for sample files + * + * @author Jitendra Kotamraju + */ +public class JsonSamplesParsingTest extends TestCase { + + public void testSampleFiles() { + String[] fileNames = { + "facebook.json", "facebook1.json", "facebook2.json", + "twitter.json" + }; + for(String fileName: fileNames) { + try { + testSampleFile(fileName); + } catch(Exception e) { + throw new JsonException("Exception while parsing "+fileName, e); + } + } + } + + private void testSampleFile(String fileName) { + Reader reader = new InputStreamReader( + JsonSamplesParsingTest.class.getResourceAsStream("/"+fileName), StandardCharsets.UTF_8); + JsonParser parser = null; + try { + parser = Json.createParser(reader); + while(parser.hasNext()) { + parser.next(); + } + } finally { + if (parser != null) { + parser.close(); + } + } + } + +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonStringTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonStringTest.java new file mode 100644 index 00000000..c982e0ee --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonStringTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import junit.framework.TestCase; + +import javax.json.*; +import java.io.StringReader; + +/** + * @author Jitendra Kotamraju + */ +public class JsonStringTest extends TestCase { + public JsonStringTest(String testName) { + super(testName); + } + + // tests JsonString#toString() + public void testToString() throws Exception { + escapedString(""); + escapedString("abc"); + escapedString("abc\f"); + escapedString("abc\na"); + escapedString("abc\tabc"); + escapedString("abc\n\tabc"); + escapedString("abc\n\tabc\r"); + escapedString("\n\tabc\r"); + escapedString("\bab\tb\rc\\\"\ftesting1234"); + escapedString("\f\babcdef\tb\rc\\\"\ftesting1234"); + escapedString("\u0000\u00ff"); + escapedString("abc\"\\/abc"); + } + + void escapedString(String str) throws Exception { + JsonArray exp = Json.createArrayBuilder().add(str).build(); + String parseStr = "["+exp.get(0).toString()+"]"; + JsonReader jr = Json.createReader(new StringReader(parseStr)); + JsonArray got = jr.readArray(); + assertEquals(exp, got); + jr.close(); + } + +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonValueTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonValueTest.java new file mode 100644 index 00000000..5a5174df --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonValueTest.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2016, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Collections; +import javax.json.JsonObject; +import javax.json.JsonString; +import javax.json.JsonValue; +import org.junit.Assert; +import org.junit.Test; + +/** + * + * @author Lukas Jungmann + */ +public class JsonValueTest { + + @Test(expected = IndexOutOfBoundsException.class) + public void arrayGetJsonObjectIdx() { + JsonValue.EMPTY_JSON_ARRAY.getJsonObject(0); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void arrayGetJsonArrayIdx() { + JsonValue.EMPTY_JSON_ARRAY.getJsonArray(0); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void arrayGetJsonNumberIdx() { + JsonValue.EMPTY_JSON_ARRAY.getJsonNumber(0); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void arrayGetJsonStringIdx() { + JsonValue.EMPTY_JSON_ARRAY.getJsonString(0); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void arrayGetStringIdx() { + JsonValue.EMPTY_JSON_ARRAY.getString(0); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void arrayGetIntIdx() { + JsonValue.EMPTY_JSON_ARRAY.getInt(0); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void arrayGetBooleanIdx() { + JsonValue.EMPTY_JSON_ARRAY.getBoolean(0); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void arrayIsNull() { + JsonValue.EMPTY_JSON_ARRAY.isNull(0); + } + + @Test + public void arrayMethods() { + Assert.assertEquals(JsonValue.ValueType.ARRAY, JsonValue.EMPTY_JSON_ARRAY.getValueType()); + Assert.assertEquals(Collections.emptyList(), JsonValue.EMPTY_JSON_ARRAY.getValuesAs(JsonObject.class)); + Assert.assertEquals(Collections.emptyList(), JsonValue.EMPTY_JSON_ARRAY.getValuesAs(JsonString::getString)); + Assert.assertEquals(true, JsonValue.EMPTY_JSON_ARRAY.getBoolean(0, true)); + Assert.assertEquals(42, JsonValue.EMPTY_JSON_ARRAY.getInt(0, 42)); + Assert.assertEquals("Sasek", JsonValue.EMPTY_JSON_ARRAY.getString(0, "Sasek")); + } + + @Test(expected = UnsupportedOperationException.class) + public void arrayIsImmutable() { + JsonValue.EMPTY_JSON_ARRAY.add(JsonValue.EMPTY_JSON_OBJECT); + } + + @Test(expected = NullPointerException.class) + public void objectGetString() { + JsonValue.EMPTY_JSON_OBJECT.getString("normalni string"); + } + + @Test(expected = NullPointerException.class) + public void objectGetInt() { + JsonValue.EMPTY_JSON_OBJECT.getInt("hledej cislo"); + } + + @Test(expected = NullPointerException.class) + public void objectGetBoolean() { + JsonValue.EMPTY_JSON_OBJECT.getBoolean("booo"); + } + + @Test(expected = NullPointerException.class) + public void objectIsNull() { + JsonValue.EMPTY_JSON_OBJECT.isNull("???"); + } + + @Test + public void objectMethods() { + Assert.assertNull(JsonValue.EMPTY_JSON_OBJECT.getJsonArray("pole")); + Assert.assertNull(JsonValue.EMPTY_JSON_OBJECT.getJsonObject("objekt")); + Assert.assertNull(JsonValue.EMPTY_JSON_OBJECT.getJsonNumber("cislo")); + Assert.assertNull(JsonValue.EMPTY_JSON_OBJECT.getJsonString("divnej string")); + + Assert.assertEquals("ja jo", JsonValue.EMPTY_JSON_OBJECT.getString("nejsem tu", "ja jo")); + Assert.assertEquals(false, JsonValue.EMPTY_JSON_OBJECT.getBoolean("najdes mne", false)); + Assert.assertEquals(98, JsonValue.EMPTY_JSON_OBJECT.getInt("spatnej dotaz", 98)); + } + + + @Test(expected = UnsupportedOperationException.class) + public void objectImmutable() { + JsonValue.EMPTY_JSON_OBJECT.put("klauni", JsonValue.EMPTY_JSON_ARRAY); + } + + @Test + public void serialization() { + byte[] data = serialize(JsonValue.TRUE); + JsonValue value = deserialize(JsonValue.class, data); + Assert.assertEquals(JsonValue.TRUE, value); + + data = serialize(JsonValue.FALSE); + value = deserialize(JsonValue.class, data); + Assert.assertEquals(JsonValue.FALSE, value); + + data = serialize(JsonValue.NULL); + value = deserialize(JsonValue.class, data); + Assert.assertEquals(JsonValue.NULL, value); + + data = serialize(JsonValue.EMPTY_JSON_ARRAY); + value = deserialize(JsonValue.class, data); + Assert.assertEquals(JsonValue.EMPTY_JSON_ARRAY, value); + + data = serialize(JsonValue.EMPTY_JSON_OBJECT); + value = deserialize(JsonValue.class, data); + Assert.assertEquals(JsonValue.EMPTY_JSON_OBJECT, value); + } + + private byte[] serialize(Object o) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { + oos.writeObject(o); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + return baos.toByteArray(); + } + + private T deserialize(Class type, byte[] data) { + ByteArrayInputStream bais = new ByteArrayInputStream(data); + try (ObjectInputStream ois = new ObjectInputStream(bais)) { + return (T) ois.readObject(); + } catch (IOException | ClassNotFoundException e) { + throw new RuntimeException(e); + } + } +} diff --git a/tests/src/test/java/org/glassfish/json/tests/JsonWriterTest.java b/tests/src/test/java/org/glassfish/json/tests/JsonWriterTest.java new file mode 100644 index 00000000..e74b0ce8 --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/JsonWriterTest.java @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import javax.json.*; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.StringWriter; + +import junit.framework.TestCase; + +/** + * @author Jitendra Kotamraju + */ +public class JsonWriterTest extends TestCase { + public JsonWriterTest(String testName) { + super(testName); + } + + public void testObject() throws Exception { + StringWriter writer = new StringWriter(); + JsonWriter jsonWriter = Json.createWriter(writer); + jsonWriter.writeObject(Json.createObjectBuilder().build()); + jsonWriter.close(); + writer.close(); + + assertEquals("{}", writer.toString()); + } + + public void testEmptyObject() throws Exception { + StringWriter writer = new StringWriter(); + JsonWriter jsonWriter = Json.createWriter(writer); + jsonWriter.write(JsonValue.EMPTY_JSON_OBJECT); + jsonWriter.close(); + writer.close(); + + assertEquals("{}", writer.toString()); + } + + public void testArray() throws Exception { + StringWriter writer = new StringWriter(); + JsonWriter jsonWriter = Json.createWriter(writer); + jsonWriter.writeArray(Json.createArrayBuilder().build()); + jsonWriter.close(); + writer.close(); + + assertEquals("[]", writer.toString()); + } + + public void testEmptyArray() throws Exception { + StringWriter writer = new StringWriter(); + JsonWriter jsonWriter = Json.createWriter(writer); + jsonWriter.write(JsonValue.EMPTY_JSON_ARRAY); + jsonWriter.close(); + writer.close(); + + assertEquals("[]", writer.toString()); + } + + public void testNumber() throws Exception { + StringWriter writer = new StringWriter(); + JsonWriter jsonWriter = Json.createWriter(writer); + jsonWriter.writeArray(Json.createArrayBuilder().add(10).build()); + jsonWriter.close(); + writer.close(); + + assertEquals("[10]", writer.toString()); + } + + public void testDoubleNumber() throws Exception { + StringWriter writer = new StringWriter(); + JsonWriter jsonWriter = Json.createWriter(writer); + jsonWriter.writeArray(Json.createArrayBuilder().add(10.5).build()); + jsonWriter.close(); + writer.close(); + + assertEquals("[10.5]", writer.toString()); + } + + public void testArrayString() throws Exception { + StringWriter writer = new StringWriter(); + JsonWriter jsonWriter = Json.createWriter(writer); + jsonWriter.writeArray(Json.createArrayBuilder().add("string").build()); + jsonWriter.close(); + writer.close(); + + assertEquals("[\"string\"]", writer.toString()); + } + + public void testObjectAsValue() throws Exception { + StringWriter writer = new StringWriter(); + JsonWriter jsonWriter = Json.createWriter(writer); + jsonWriter.write((JsonValue) (Json.createObjectBuilder().build())); + jsonWriter.close(); + writer.close(); + + assertEquals("{}", writer.toString()); + } + + public void testNullValue() throws Exception { + StringWriter writer = new StringWriter(); + JsonWriter jsonWriter = Json.createWriter(writer); + jsonWriter.write(JsonValue.NULL); + jsonWriter.close(); + writer.close(); + + assertEquals("null", writer.toString()); + } + + public void testTrueValue() throws Exception { + StringWriter writer = new StringWriter(); + JsonWriter jsonWriter = Json.createWriter(writer); + jsonWriter.write(JsonValue.TRUE); + jsonWriter.close(); + writer.close(); + + assertEquals("true", writer.toString()); + } + + public void testFalseValue() throws Exception { + StringWriter writer = new StringWriter(); + JsonWriter jsonWriter = Json.createWriter(writer); + jsonWriter.write(JsonValue.FALSE); + jsonWriter.close(); + writer.close(); + + assertEquals("false", writer.toString()); + } + + public void testIllegalStateExcepton() throws Exception { + JsonObject obj = Json.createObjectBuilder().build(); + JsonArray array = Json.createArrayBuilder().build(); + + JsonWriter writer = Json.createWriter(new StringWriter()); + writer.writeObject(obj); + try { + writer.writeObject(obj); + } catch (IllegalStateException expected) { + // no-op + } + writer.close(); + + writer = Json.createWriter(new StringWriter()); + writer.writeArray(array); + try { + writer.writeArray(array); + } catch (IllegalStateException expected) { + // no-op + } + writer.close(); + + writer = Json.createWriter(new StringWriter()); + writer.write(array); + try { + writer.writeArray(array); + } catch (IllegalStateException expected) { + // no-op + } + writer.close(); + } + + public void testNoCloseWriteObjectToStream() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + JsonWriter writer = Json.createWriter(baos); + writer.write(Json.createObjectBuilder().build()); + // not calling writer.close() intentionally + assertEquals("{}", baos.toString("UTF-8")); + } + + public void testNoCloseWriteObjectToWriter() throws Exception { + StringWriter sw = new StringWriter(); + JsonWriter writer = Json.createWriter(sw); + writer.write(Json.createObjectBuilder().build()); + // not calling writer.close() intentionally + assertEquals("{}", sw.toString()); + } + + public void testNoCloseWriteArrayToStream() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + JsonWriter writer = Json.createWriter(baos); + writer.write(Json.createArrayBuilder().build()); + // not calling writer.close() intentionally + assertEquals("[]", baos.toString("UTF-8")); + } + + public void testNoCloseWriteArrayToWriter() throws Exception { + StringWriter sw = new StringWriter(); + JsonWriter writer = Json.createWriter(sw); + writer.write(Json.createArrayBuilder().build()); + // not calling writer.close() intentionally + assertEquals("[]", sw.toString()); + } + + public void testClose() throws Exception { + MyByteStream baos = new MyByteStream(); + JsonWriter writer = Json.createWriter(baos); + writer.write(Json.createObjectBuilder().build()); + writer.close(); + assertEquals("{}", baos.toString("UTF-8")); + assertTrue(baos.isClosed()); + } + + private static final class MyByteStream extends ByteArrayOutputStream { + boolean closed; + + boolean isClosed() { + return closed; + } + + public void close() throws IOException { + super.close(); + closed = true; + } + } +} diff --git a/tests/src/test/java/org/glassfish/json/tests/RFC7159Test.java b/tests/src/test/java/org/glassfish/json/tests/RFC7159Test.java new file mode 100644 index 00000000..80e3a5e5 --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/RFC7159Test.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import org.junit.Test; +import org.junit.BeforeClass; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import javax.json.*; +import javax.json.stream.JsonGenerator; +import java.io.StringWriter; +import java.io.StringReader; + +/** + * @author Kin-man Chung + */ +public class RFC7159Test { + + @Test + public void testCreatValues() { + JsonArrayBuilder builder = Json.createArrayBuilder(); + JsonArray array = builder.add(Json.createValue("someString")) + .add(Json.createValue(100)) + .add(Json.createValue(12345.6789)) + .build(); + builder = Json.createArrayBuilder(); + JsonArray expected = builder.add("someString") + .add(100) + .add(12345.6789) + .build(); + assertEquals(expected, array); + } + + @Test + public void testReadValues() { + JsonReader reader = Json.createReader(new StringReader("\"someString\"")); + JsonArrayBuilder builder = Json.createArrayBuilder(); + builder.add(reader.readValue()); + reader = Json.createReader(new StringReader("100")); + builder.add(reader.readValue()); + reader = Json.createReader(new StringReader("12345.6789")); + builder.add(reader.readValue()); + JsonArray array = builder.build(); + builder = Json.createArrayBuilder(); + JsonArray expected = builder.add("someString") + .add(100) + .add(12345.6789) + .build(); + assertEquals(expected, array); + } + + @Test + public void testWriteValues() { + StringWriter stringWriter = new StringWriter(); + JsonWriter writer = Json.createWriter(stringWriter); + writer.write(Json.createValue("someString")); + assertEquals("\"someString\"", stringWriter.toString()); + + stringWriter = new StringWriter(); + writer = Json.createWriter(stringWriter); + writer.write(Json.createValue(100)); + assertEquals("100", stringWriter.toString()); + + stringWriter = new StringWriter(); + writer = Json.createWriter(stringWriter); + writer.write(Json.createValue(12345.6789)); + assertEquals("12345.6789", stringWriter.toString()); + } + + @Test + public void testGeneratorValues() { + StringWriter stringWriter = new StringWriter(); + JsonGenerator generator = Json.createGenerator(stringWriter); + generator.write("someString").close(); + assertEquals("\"someString\"", stringWriter.toString()); + + stringWriter = new StringWriter(); + generator = Json.createGenerator(stringWriter); + generator.write(100).close(); + assertEquals("100", stringWriter.toString()); + + stringWriter = new StringWriter(); + generator = Json.createGenerator(stringWriter); + generator.write(12345.6789).close(); + assertEquals("12345.6789", stringWriter.toString()); + } +} diff --git a/tests/src/test/java/org/glassfish/json/tests/ToJsonTest.java b/tests/src/test/java/org/glassfish/json/tests/ToJsonTest.java new file mode 100644 index 00000000..47a97a23 --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/ToJsonTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import org.junit.Test; + +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonArrayBuilder; +import javax.json.JsonValue; + +import org.glassfish.json.JsonUtil; + +import static org.junit.Assert.assertEquals; +/** + * @author Kin-man Chung + */ +public class ToJsonTest { + + @Test + public void testToJson() { + assertEquals(Json.createValue("someString"), JsonUtil.toJson("'someString'")); + assertEquals(Json.createValue("some'thing"), JsonUtil.toJson("'some\\'thing'")); + assertEquals(Json.createValue("some\"thing"), JsonUtil.toJson("'some\\\"thing'")); + JsonArrayBuilder builder = Json.createArrayBuilder(); + JsonArray array = builder + .add(Json.createObjectBuilder() + .add("name", "John") + .add("age", 35) + .add("educations", Json.createArrayBuilder() + .add("Gunn High") + .add("UC Berkeley"))) + .add(Json.createObjectBuilder() + .add("name", "Jane") + .add("educations", Json.createArrayBuilder() + .add("Oxford"))) + .build(); + JsonValue expected = JsonUtil.toJson( + "[ { 'name': 'John', " + + "'age': 35, " + + "'educations': ['Gunn High', 'UC Berkeley'] }, " + + " { 'name': 'Jane', " + + "'educations': ['Oxford']}]"); + assertEquals(expected, array); + } +} + diff --git a/tests/src/test/java/org/glassfish/json/tests/TwitterSearchTest.java b/tests/src/test/java/org/glassfish/json/tests/TwitterSearchTest.java new file mode 100644 index 00000000..33574d26 --- /dev/null +++ b/tests/src/test/java/org/glassfish/json/tests/TwitterSearchTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.json.tests; + +import junit.framework.TestCase; + +import javax.json.*; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; +import java.io.*; +import java.net.URL; + +/** + * JsonParser Tests using twitter search API + * + * @author Jitendra Kotamraju + */ +public class TwitterSearchTest extends TestCase { + + public void test() { + // dummy test so that junit doesn't complain + } + + public void xtestStreamTwitter() throws Exception { + URL url = new URL("http://search.twitter.com/search.json?q=%23java&rpp=100"); + InputStream is = url.openStream(); + JsonParser parser = Json.createParser(is); + + while(parser.hasNext()) { + Event e = parser.next(); + if (e == Event.KEY_NAME) { + if (parser.getString().equals("from_user")) { + parser.next(); + System.out.print(parser.getString()); + System.out.print(": "); + } else if (parser.getString().equals("text")) { + parser.next(); + System.out.println(parser.getString()); + System.out.println("---------"); + } + } + } + parser.close(); + } + + public void xtestObjectTwitter() throws Exception { + URL url = new URL("http://search.twitter.com/search.json?q=%23java&rpp=100"); + InputStream is = url.openStream(); + JsonReader rdr = Json.createReader(is); + JsonObject obj = rdr.readObject(); + JsonArray results = obj.getJsonArray("results"); + for(JsonObject result : results.getValuesAs(JsonObject.class)) { + System.out.print(result.get("from_user")); + System.out.print(": "); + System.out.println(result.get("text")); + System.out.println("-----------"); + } + rdr.close(); + } + +} diff --git a/tests/src/test/resources/facebook.json b/tests/src/test/resources/facebook.json new file mode 100644 index 00000000..de667ebf --- /dev/null +++ b/tests/src/test/resources/facebook.json @@ -0,0 +1,668 @@ +{ + "data": [ + { + "id": "540450616_10151581659930617", + "from": { + "name": "Ankit Mehta", + "id": "540450616" + }, + "message": "Times flies so fast as Tomorrow will be completing one week in India and still missing uk and my home 5 egret house and friends like andria, chital, Charmi, Kushal, and munjal. Hope to see you guys soon in india. But on the other side happy to be back with my mom dad and sis and my sweet home after very long long time.", + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Facebook for iPhone", + "namespace": "fbiphone", + "id": "6628568379" + }, + "created_time": "2013-07-26T22:33:09+0000", + "updated_time": "2013-07-26T22:33:09+0000" + }, + { + "id": "143528829157622_184268238417014", + "from": { + "category": "Author", + "name": "Kabi Kisi Ko Mukamal Janha Nahi Milta", + "id": "143528829157622" + }, + "message": "Top 10 Masajid Of\nd World With Capacity\nof Namaziis\n...\n1. KHANA-e-KABA\n(MASJID ALHARAM)\n13,50,000\n2. MASJID-E-NABVI\n7,98,000\n3. MASJID AL MAKKAH\nKualalumpur , Malaysia\n5,38,000\n4. MASJID AL\nSIDDQUE-e-AKBAR\nParis , France\n4,95,000\n5. JAMIA UMER\nFAROOQ\nWashington , USA\n4,50,000\n6. ISLAMIC RESEARCH\nCENTRE,\nWorkshire, Uk\n4,30,000\n7. HISTORIC MOSQUE\nNew Delhi India\n4,00,000\n8. SHAH FAHAD MASJID\nRiyadh, Saudia Arabia\n3,70,000\n9. SHAH FAISAL MASJID\nIslamabad, Pakistan\n2,95,000\n10. JAMIA MASJID USMANI\nMumbai , India\n2,73,000\nJAZAKALLAH KHAIR", + "actions": [ + { + "name": "Comment", + "link": "https://www.facebook.com/143528829157622/posts/184268238417014" + } + ], + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Mobile", + "id": "2915120374" + }, + "created_time": "2013-07-26T22:33:03+0000", + "updated_time": "2013-07-26T22:33:03+0000" + }, + { + "id": "540006262732189_558758910856924", + "from": { + "category": "Community", + "name": "BLUE Team", + "id": "540006262732189" + }, + "message": "India-Zimbabwe 4th ODI postponed due to elections\n\nThe fourth one-day international of the ongoing series between India and Zimbabwe has been postponed by a day due to general elections in the African country.\nThe match will now be played at the Queens Sports Club, Bulawayo, on August 1 instead ofthe original date of July 31.\n\"The fourth ODI of the ongoing series between India and Zimbabwe has been postponed by a day on account of Elections in Zimbabwe,\" a BCCI release said.\nIndia lead the series 1-0 after defeating the hosts by six wickets in the first one-dayer on Wednesday.", + "actions": [ + { + "name": "Comment", + "link": "https://www.facebook.com/540006262732189/posts/558758910856924" + } + ], + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Mobile", + "id": "2915120374" + }, + "created_time": "2013-07-26T22:33:00+0000", + "updated_time": "2013-07-26T22:33:00+0000" + }, + { + "id": "485447954806489_625747810776502", + "from": { + "category": "Bank/financial institution", + "name": "Payday loan business owners collection advice", + "id": "485447954806489" + }, + "message": "about viagra super active http://about.viagra.super.active.medoonll4.appspot.com/#about-viagra-super-active\nviagra covered insurance http://viagra.covered.insurance.medoonll4.appspot.com/#viagra-covered-insurance\nhalf a viagra pill http://half.a.viagra.pill.medoonll4.appspot.com/#half-a-viagra-pill\ncanada buy cialis soft http://canada.buy.cialis.soft.medoonll4.appspot.com/#canada-buy-cialis-soft\nimpotence treatments vitamins http://impotence.treatments.vitamins.medoonll4.appspot.com/#impotence-treatments-vitamins\nwomen liquid viagra http://women.liquid.viagra.medoonll4.appspot.com/#women-liquid-viagra\nzoloft specialist http://zoloft.specialist.medoonll4.appspot.com/#zoloft-specialist\ncialis dosage advice http://cialis.dosage.advice.medoonll4.appspot.com/#cialis-dosage-advice\ncialis prescription cheap http://cialis.prescription.cheap.medoonll4.appspot.com/#cialis-prescription-cheap\nviagra for sale pay pal http://viagra.for.sale.pay.pal.medoonll4.appspot.com/#viagra-for-sale-pay-pal\ncheapest viagra kamagra http://cheapest.viagra.kamagra.medoonll4.appspot.com/#cheapest-viagra-kamagra\neffect cialis women http://effect.cialis.women.medoonll4.appspot.com/#effect-cialis-women\nlevitra word meaning http://levitra.word.meaning.medoonll4.appspot.com/#levitra-word-meaning\nviagra pill tesco http://viagra.pill.tesco.medoonll4.appspot.com/#viagra-pill-tesco\nviagra vs cialis cialis http://viagra.vs.cialis.cialis.medoonll4.appspot.com/#viagra-vs-cialis-cialis\nbuy levitra online us pharmacy http://buy.levitra.online.us.pharmacy.medoonll4.appspot.com/#buy-levitra-online-us-pharmacy\nviagra radiator http://viagra.radiator.medoonll4.appspot.com/#viagra-radiator\ninformation on cialis tadalafil http://information.on.cialis.tadalafil.medoonll4.appspot.com/#information-on-cialis-tadalafil\n365pills problems http://365pills.problems.medoonll4.appspot.com/#365pills-problems\nviagra buy melbourne http://viagra.buy.melbourne.medoonll4.appspot.com/#viagra-buy-melbourne\npropecia off patent http://propecia.off.patent.medoonll4.appspot.com/#propecia-off-patent\ncialis tablets sale http://cialis.tablets.sale.medoonll4.appspot.com/#cialis-tablets-sale\nmeds 24 online viagra sales http://meds.24.online.viagra.sales.medoonll4.appspot.com/#meds-24-online-viagra-sales\ngeneric cialis greece http://generic.cialis.greece.medoonll4.appspot.com/#generic-cialis-greece\nviagra india online university http://viagra.india.online.university.medoonll4.appspot.com/#viagra-india-online-university", + "actions": [ + { + "name": "Comment", + "link": "https://www.facebook.com/485447954806489/posts/625747810776502" + } + ], + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Seesmic", + "id": "23723376453" + }, + "created_time": "2013-07-26T22:32:58+0000", + "updated_time": "2013-07-26T22:32:58+0000" + }, + { + "id": "682077629_10151750832397630", + "from": { + "name": "Angela Aline Black", + "id": "682077629" + }, + "message": "20. The economic success of the Mughals in India was dependent upon \n\n \n \nA. the success of external markets. \nB. low levels of taxation. \nC. success of exporters. \nD. economic success of subjects under their rule", + "privacy": { + "value": "" + }, + "type": "status", + "created_time": "2013-07-26T22:32:55+0000", + "updated_time": "2013-07-26T22:32:55+0000" + }, + { + "id": "100002907425513_442348052538772", + "from": { + "name": "Sanjay Suneja Insan", + "id": "100002907425513" + }, + "message": "dhan-dhan \"SATGURU\" tera hi aasra, Menu bhule na serjan hara bhul jawe sari duniya , sari duniya sari duniya ,.. Ghar kam bhul k paap c karda kaal di vagar dho k khap khap marda , kaal desh vich kuchh nahi hamara k bhul jave sari dunia ... ( shabad . vinti bhajan, dera sacha sauda, sirsa, haryana, india )", + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Mobile", + "id": "2915120374" + }, + "created_time": "2013-07-26T22:32:46+0000", + "updated_time": "2013-07-26T22:32:46+0000" + }, + { + "id": "571804039505307_634527159899661", + "from": { + "category": "Community", + "name": "\u0e2a\u0e32\u0e27\u0e19\u0e49\u0e2d\u0e22\u0e15\u0e01\u0e19\u0e49\u0e33", + "id": "571804039505307" + }, + "message": "drive without auto insurance http://how.much.auto.insurance.do.i.need.2013.insurancecheapautocar.appspot.com is auto insurance tax deductible http://how.much.auto.insurance.do.i.need.in.new.york.insurancecheapautocar.appspot.com auto insurance in bradenton http://how.much.auto.insurance.deductible.insurancecheapautocar.appspot.com whats the cheapest car insurance in kentucky http://how.much.auto.insurance.coverage.is.enough.insurancecheapautocar.appspot.com edmonton cheapest auto insurance http://how.much.auto.insurance.florida.insurancecheapautocar.appspot.com federal insurance company auto claims http://how.much.is.auto.insurance.from.state.farm.insurancecheapautocar.appspot.com insurance auto auction reno nv http://how.much.for.auto.insurance.insurancecheapautocar.appspot.com cheapest car insurance for young drivers in ireland http://how.much.coverage.for.auto.insurance.insurancecheapautocar.appspot.com erie auto insurance coverage http://how.much.auto.insurance.goes.up.after.accident.insurancecheapautocar.appspot.com national union fire insurance company auto claims http://how.much.will.my.auto.insurance.go.up.insurancecheapautocar.appspot.com car insurance companies in india http://how.much.auto.insurance.coverage.should.i.have.insurancecheapautocar.appspot.com auto accident claim book http://how.to.determine.how.much.auto.insurance.coverage.insurancecheapautocar.appspot.com auto insurance rates across canada http://how.much.auto.insurance.is.necessary.insurancecheapautocar.appspot.com allstate auto insurance claims department address http://how.much.auto.insurance.is.sufficient.insurancecheapautocar.appspot.com hartford car insurance sign in http://how.do.i.know.how.much.auto.insurance.i.need.insurancecheapautocar.appspot.com safeway auto insurance website http://how.do.i.know.how.much.auto.insurance.coverage.i.need.insurancecheapautocar.appspot.com car insurance average for teenagers http://how.do.you.know.how.much.auto.insurance.you.need.insurancecheapautocar.appspot.com best value car insurance for over 50s http://how.much.auto.insurance.per.month.insurancecheapautocar.appspot.com best value car insurance sydney http://how.much.will.my.auto.insurance.increase.after.an.accident.insurancecheapautocar.appspot.com car insurance quotes in rochester ny http://how.much.auto.insurance.do.i.need.2011.insurancecheapautocar.appspot.com auto insurance requirements for loan http://how.much.auto.insurance.do.i.need.florida.insurancecheapautocar.appspot.com", + "actions": [ + { + "name": "Comment", + "link": "https://www.facebook.com/571804039505307/posts/634527159899661" + } + ], + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "DoubleQ", + "namespace": "doubleqapp", + "id": "417924791618438" + }, + "created_time": "2013-07-26T22:32:41+0000", + "updated_time": "2013-07-26T22:32:41+0000" + }, + { + "id": "100002550173862_479466595481675", + "from": { + "name": "Duygu K\u00f6sem", + "id": "100002550173862" + }, + "message": "Bug\u00fcn g\u00fclden ben hatice merve kubra gubnesliparkta rezil olduk hep bu kubranin so\u011fuk espirileri y\u00fcz\u00fcnden :( ;( ;( donerken bide o yetmedi india yi s\u00f6yleye s\u00f6yleye geldik sonra bi \u00e7ocuk bana laf att\u0131 bende ba\u011f\u0131rd\u0131m g\u00fclden benim taklidimi yapt\u0131 g\u00fclmekten yarildim merveyle kubra baska alemlerdeydi yani bug\u00fcn g\u00fczelde olsa i\u011fren\u00e7 bi g\u00fcnd\u00fc", + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Mobile", + "id": "2915120374" + }, + "created_time": "2013-07-26T22:32:36+0000", + "updated_time": "2013-07-26T22:32:36+0000" + }, + { + "id": "318123048285301_448973998533538", + "from": { + "category": "Bank/financial institution", + "name": "Usa realty and loans san diego ca", + "id": "318123048285301" + }, + "message": "tadalafil cialis from india http://tadalafil.cialis.from.india.medoonll4.appspot.com/#tadalafil-cialis-from-india\npropecia online pharmacy http://propecia.online.pharmacy.medoonll4.appspot.com/#propecia-online-pharmacy\nviagra 0.74 http://viagra.0.74.medoonll4.appspot.com/#viagra-0.74\nfree viagra on nhs http://free.viagra.on.nhs.medoonll4.appspot.com/#free-viagra-on-nhs\nviagra spicy foods http://viagra.spicy.foods.medoonll4.appspot.com/#viagra-spicy-foods\ngetting viagra singapore http://getting.viagra.singapore.medoonll4.appspot.com/#getting-viagra-singapore\nbest cialis ordering sites http://best.cialis.ordering.sites.medoonll4.appspot.com/#best-cialis-ordering-sites\nwhy viagra and cialis http://why.viagra.and.cialis.medoonll4.appspot.com/#why-viagra-and-cialis\nrecommended doses of viagra http://recommended.doses.of.viagra.medoonll4.appspot.com/#recommended-doses-of-viagra\nviagra fakten http://viagra.fakten.medoonll4.appspot.com/#viagra-fakten\npfizer lipitor viagra http://pfizer.lipitor.viagra.medoonll4.appspot.com/#pfizer-lipitor-viagra\ndoes propecia work good http://does.propecia.work.good.medoonll4.appspot.com/#does-propecia-work-good\nwhere to get viagra ireland http://where.to.get.viagra.ireland.medoonll4.appspot.com/#where-to-get-viagra-ireland\nviagra length of effect http://viagra.length.of.effect.medoonll4.appspot.com/#viagra-length-of-effect\nlevitra package size http://levitra.package.size.medoonll4.appspot.com/#levitra-package-size\nlevitra skin rash http://levitra.skin.rash.medoonll4.appspot.com/#levitra-skin-rash\ncialis c20 pill http://cialis.c20.pill.medoonll4.appspot.com/#cialis-c20-pill\nviagra 100mg prices http://viagra.100mg.prices.medoonll4.appspot.com/#viagra-100mg-prices\nviagra spray commercial http://viagra.spray.commercial.medoonll4.appspot.com/#viagra-spray-commercial\nbuy viagra in scotland http://buy.viagra.in.scotland.medoonll4.appspot.com/#buy-viagra-in-scotland\nalprazolam viagra http://alprazolam.viagra.medoonll4.appspot.com/#alprazolam-viagra\npro and cons of viagra http://pro.and.cons.of.viagra.medoonll4.appspot.com/#pro-and-cons-of-viagra\nviagra gymnast http://viagra.gymnast.medoonll4.appspot.com/#viagra-gymnast\ncialis viagra cheap http://cialis.viagra.cheap.medoonll4.appspot.com/#cialis-viagra-cheap\nfree levitra pen http://free.levitra.pen.medoonll4.appspot.com/#free-levitra-pen", + "actions": [ + { + "name": "Comment", + "link": "https://www.facebook.com/318123048285301/posts/448973998533538" + } + ], + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Status Shuffle", + "namespace": "status-shuffle", + "id": "8109791468" + }, + "created_time": "2013-07-26T22:32:30+0000", + "updated_time": "2013-07-26T22:32:30+0000" + }, + { + "id": "100001499151848_548923935167573", + "from": { + "name": "Piyush Tiwari", + "id": "100001499151848" + }, + "message": "MP High-Court District Legal Aid Officer posts July-2013\nPosted: 26 Jul 2013 09:04 AM PDT\nPublished for http://www.SarkariNaukriBlog.com \nHigh Court of Madhya Pradesh, Jabalpur\n\nRecruitment of District Legal Aid Officer \n\nOnline Applications are invited at MPOnline Portal from Indian Citizens for posts in Madhya Pradesh State Legal Service Authority through the Online preliminary exam - on 15/09/2013 and final exam on 27/10/2013 :\nDistrict Legal Aid Officer : 13 posts, Pay Scale : Rs. 9300-34800 grade pay Rs. 4200/-, Age : 21-35 years as on 01/01/2014.\n\nApplication Fee : Rs. 800/- (Rs. 700/- for candidates belonging to Reserved Category). The candidates shall have to pay Rs. 60/- as portal charge, in addition to aforesaid Examination fees. \n\nHow to Apply: Apply Online at MPOnline website up to 12/08/2013 only. \n\nFor further information, please view http://www.mphighcourt.nic.in/legalAssitt-090713.pdf and apply online, kindly visit http://www.mponline.gov.in \n\nPublished at http://www.SarkariNaukriBlog.com \n(Click on the Labels below for more similar Jobs)\nCompiled by Sarkari-Naukri for blog http://www.sarkarinaukriblog.com/ for providing Government Jobs available in India. . Other Blogs are\n\nhttp://www.HindiDiary.com and Friendly blog is http://bankjob.blogspot.com\n\nAt Facebook at http://www.facebook.com/sarkarinaukri.india", + "picture": "https://fbexternal-a.akamaihd.net/safe_image.php?d=AQAntDPz9j06aZeb&w=154&h=154&url=http\u00253A\u00252F\u00252Fbuttons.blogger.com\u00252Fbloggerbutton1.gif", + "link": "http://www.sarkarinaukriblog.com/", + "name": "\u0938\u0930\u0915\u093e\u0930\u0940 \u0928\u094c\u0915\u0930\u0940 - Government Jobs India - Sarkari Naukri -- www.SarkariNaukriBlog.com", + "caption": "www.sarkarinaukriblog.com", + "description": "www.SarkariNaukriBlog.com - Website about all Sarkari and Government Jobs in Central/State Government, Universities, Public Sector Companies and Banks, jobs, vacancy, opportunity, Government, Government of India, Recruitment, State Government, Central Government, India", + "icon": "https://fbstatic-a.akamaihd.net/rsrc.php/v2/yD/r/aS8ecmYRys0.gif", + "privacy": { + "value": "" + }, + "type": "link", + "created_time": "2013-07-26T22:32:14+0000", + "updated_time": "2013-07-26T22:32:14+0000" + }, + { + "id": "679382908744276_698464940169406", + "from": { + "category": "Community", + "name": "Tinnitustreatmentreviews", + "id": "679382908744276" + }, + "message": "Top 10 Dating Site India http://bit.ly/1bWKegR", + "actions": [ + { + "name": "Comment", + "link": "https://www.facebook.com/679382908744276/posts/698464940169406" + } + ], + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "twitterfeed", + "id": "131732509879" + }, + "created_time": "2013-07-26T22:32:13+0000", + "updated_time": "2013-07-26T22:32:13+0000" + }, + { + "id": "563417097011729_597056126981159", + "from": { + "category": "Entertainer", + "name": "Jiya Na Jaye Tum Bin,", + "id": "563417097011729" + }, + "message": "Jai ho india mata\n\nbatao ye kiska nara hai jo ki fb me fast time lagaya tha.", + "actions": [ + { + "name": "Comment", + "link": "https://www.facebook.com/563417097011729/posts/597056126981159" + } + ], + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Mobile", + "id": "2915120374" + }, + "created_time": "2013-07-26T22:32:10+0000", + "updated_time": "2013-07-26T22:32:10+0000" + }, + { + "id": "425879394186297_441042339336669", + "from": { + "category": "Health/beauty", + "name": "Tinnitus Treatment", + "id": "425879394186297" + }, + "message": "Top 10 Dating Site India http://bit.ly/1bWKegR", + "actions": [ + { + "name": "Comment", + "link": "https://www.facebook.com/425879394186297/posts/441042339336669" + } + ], + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "twitterfeed", + "id": "131732509879" + }, + "created_time": "2013-07-26T22:32:07+0000", + "updated_time": "2013-07-26T22:32:07+0000" + }, + { + "id": "100004032192846_293648487446222", + "from": { + "name": "Kumar Chettri", + "id": "100004032192846" + }, + "message": "India vs Zim Ind . 294 runs ind win by 58 run S Dhawan 116 man of the match total matcha 5 ind win 2 matcha cantaune", + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Mobile", + "id": "2915120374" + }, + "created_time": "2013-07-26T22:31:53+0000", + "updated_time": "2013-07-26T22:31:53+0000" + }, + { + "id": "197080493647669_611425628879818", + "from": { + "category": "Community", + "name": "Live Cricket Scores - Indian Cricket Team", + "id": "197080493647669" + }, + "message": "India-Zimbabwe 4th ODI postponed due to elections\n\nThe fourth one-day international of the ongoing series between India and Zimbabwe has been postponed by a day due to general elections in the African country.\nThe match will now be played at the Queens Sports Club, Bulawayo, on August 1 instead ofthe original date of July 31.\n\"The fourth ODI of the ongoing series between India and Zimbabwe has been postponed by a day on account of Elections in Zimbabwe,\" a BCCI release said.\nIndia lead the series 1-0 after defeating the hosts by six wickets in the first one-dayer on Wednesday.", + "actions": [ + { + "name": "Comment", + "link": "https://www.facebook.com/197080493647669/posts/611425628879818" + } + ], + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Mobile", + "id": "2915120374" + }, + "created_time": "2013-07-26T22:31:40+0000", + "updated_time": "2013-07-26T22:31:40+0000" + }, + { + "id": "100003164088972_418256498289816", + "from": { + "name": "Nandkumar Sonawane", + "id": "100003164088972" + }, + "message": "near SH-30, Jawhar, India http://her.is/UTmqa", + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Nokia", + "id": "27694818115" + }, + "created_time": "2013-07-26T22:31:39+0000", + "updated_time": "2013-07-26T22:31:39+0000" + }, + { + "id": "690368524_10151553117193525", + "from": { + "name": "Louise Kempson", + "id": "690368524" + }, + "to": { + "data": [ + { + "name": "Adrian Murphy", + "id": "503204091" + } + ] + }, + "with_tags": { + "data": [ + { + "name": "Adrian Murphy", + "id": "503204091" + } + ] + }, + "message": "Geography lesson!! Sri Lanka isn't in India?! Who knew!!!", + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Facebook for iPhone", + "namespace": "fbiphone", + "id": "6628568379" + }, + "created_time": "2013-07-26T22:31:38+0000", + "updated_time": "2013-07-26T22:31:38+0000" + }, + { + "id": "442583219125645_577412425642723", + "from": { + "category": "Bank/financial institution", + "name": "Payday cookie recipe", + "id": "442583219125645" + }, + "message": "viagra australia legality http://viagra.australia.legality.medoonll4.appspot.com/#viagra-australia-legality\npristiq and viagra http://pristiq.and.viagra.medoonll4.appspot.com/#pristiq-and-viagra\nhigh blood pressure and cialis http://high.blood.pressure.and.cialis.medoonll4.appspot.com/#high-blood-pressure-and-cialis\ngeneric viagra fast shipping http://generic.viagra.fast.shipping.medoonll4.appspot.com/#generic-viagra-fast-shipping\nnatural where to buy viagra http://natural.where.to.buy.viagra.medoonll4.appspot.com/#natural-where-to-buy-viagra\nviagra world anti-doping agency http://viagra.world.anti-doping.agency.medoonll4.appspot.com/#viagra-world-anti-doping-agency\ncialis often can taken http://cialis.often.can.taken.medoonll4.appspot.com/#cialis-often-can-taken\ncialis with flomax http://cialis.with.flomax.medoonll4.appspot.com/#cialis-with-flomax\nherbal viagra generic http://herbal.viagra.generic.medoonll4.appspot.com/#herbal-viagra-generic\nla viagra efectos secundarios http://la.viagra.efectos.secundarios.medoonll4.appspot.com/#la-viagra-efectos-secundarios\nlevitra bystolic interaction http://levitra.bystolic.interaction.medoonll4.appspot.com/#levitra-bystolic-interaction\nviagra 88 keys ft kanye west http://viagra.88.keys.ft.kanye.west.medoonll4.appspot.com/#viagra-88-keys-ft-kanye-west\nviagra for sale west midlands http://viagra.for.sale.west.midlands.medoonll4.appspot.com/#viagra-for-sale-west-midlands\nviagra vs viagra soft tabs http://viagra.vs.viagra.soft.tabs.medoonll4.appspot.com/#viagra-vs-viagra-soft-tabs\nviagra online surveys http://viagra.online.surveys.medoonll4.appspot.com/#viagra-online-surveys\ngeneric viagra silagra http://generic.viagra.silagra.medoonll4.appspot.com/#generic-viagra-silagra\ncialis cutting http://cialis.cutting.medoonll4.appspot.com/#cialis-cutting\npaypal cialis on line http://paypal.cialis.on.line.medoonll4.appspot.com/#paypal-cialis-on-line\ndoing poppers http://doing.poppers.medoonll4.appspot.com/#doing-poppers\ngeneric viagra soft online http://generic.viagra.soft.online.medoonll4.appspot.com/#generic-viagra-soft-online\nviagra healthcare reform http://viagra.healthcare.reform.medoonll4.appspot.com/#viagra-healthcare-reform\nlevitra 10mg tablets http://levitra.10mg.tablets.medoonll4.appspot.com/#levitra-10mg-tablets\ncialis age related ed http://cialis.age.related.ed.medoonll4.appspot.com/#cialis-age-related-ed\ndosing for cialis daily use http://dosing.for.cialis.daily.use.medoonll4.appspot.com/#dosing-for-cialis-daily-use\nviagra brand in india http://viagra.brand.in.india.medoonll4.appspot.com/#viagra-brand-in-india", + "actions": [ + { + "name": "Comment", + "link": "https://www.facebook.com/442583219125645/posts/577412425642723" + } + ], + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Status Shuffle", + "namespace": "status-shuffle", + "id": "8109791468" + }, + "created_time": "2013-07-26T22:31:37+0000", + "updated_time": "2013-07-26T22:31:37+0000" + }, + { + "id": "197080493647669_611425615546486", + "from": { + "category": "Community", + "name": "Live Cricket Scores - Indian Cricket Team", + "id": "197080493647669" + }, + "message": "The fourth one-day international of the ongoing series between India and Zimbabwe has been postponed by a day due to general elections in the African country.\nThe match will now be played at the Queens Sports Club, Bulawayo, on August 1 instead ofthe original date of July 31.\n\"The fourth ODI of the ongoing series between India and Zimbabwe has been postponed by a day on account of Elections in Zimbabwe,\" a BCCI release said.\nIndia lead the series 1-0 after defeating the hosts by six wickets in the first one-dayer on Wednesday.", + "actions": [ + { + "name": "Comment", + "link": "https://www.facebook.com/197080493647669/posts/611425615546486" + } + ], + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Mobile", + "id": "2915120374" + }, + "created_time": "2013-07-26T22:31:36+0000", + "updated_time": "2013-07-26T22:31:36+0000" + }, + { + "id": "100002173676538_496486443767140", + "from": { + "name": "Mely T\u00fa Mu\u00f1eca Kaulitz", + "id": "100002173676538" + }, + "message": "SAB\u00cdAS QUE...\n\nUn hombre se casa con 60 mujeres para venderlas como esposas. Esto en India.\n\nOhmaigash D:", + "privacy": { + "value": "" + }, + "type": "status", + "created_time": "2013-07-26T22:31:36+0000", + "updated_time": "2013-07-26T22:31:36+0000" + }, + { + "id": "398559080202364_590956980962572", + "from": { + "category": "Website", + "name": "Dinamani Newspaper Advertising Rate Card", + "id": "398559080202364" + }, + "message": "\u0b86\u0b95\u0bb8\u0bcd\u0b9f\u0bcd 30-\u0bb2\u0bcd \u0b9c\u0bbf\u0b9a\u0bbe\u0b9f\u0bcd-7 \u0b9a\u0bc6\u0baf\u0bb1\u0bcd\u0b95\u0bc8\u0b95\u0bcd\u0b95\u0bcb\u0bb3\u0bcd \u0b8f\u0bb5\u0baa\u0bcd\u0baa\u0b9f\u0bc1\u0bae\u0bcd: \u0b9c\u0bbf\u0b9a\u0bbe\u0b9f\u0bcd-7 \u0ba4\u0b95\u0bb5\u0bb2\u0bcd \u0ba4\u0bca\u0b9f\u0bb0\u0bcd\u0baa\u0bc1 \u0b9a\u0bc6\u0baf\u0bb1\u0bcd\u0b95\u0bc8\u0b95\u0bcd\u0b95\u0bcb\u0bb3\u0bcd \u0b86\u0b95\u0bb8\u0bcd\u0b9f\u0bcd 30-\u0b86\u0bae\u0bcd \u0ba4\u0bc7\u0ba4\u0bbf \u0bb5\u0bbf\u0ba3\u0bcd\u0ba3\u0bbf\u0bb2\u0bcd \u0b9a\u0bc6\u0bb2\u0bc1\u0ba4\u0bcd\u0ba4\u0baa\u0bcd\u0baa\u0b9f\u0bc1\u0bae\u0bcd \u0b8e\u0ba9\u0bcd\u0bb1\u0bc1 \u0b87\u0ba8\u0bcd\u0ba4\u0bbf\u0baf \u0bb5\u0bbf\u0ba3\u0bcd\u0bb5\u0bc6\u0bb3\u0bbf \u0b86\u0bb0\u0bbe\u0baf\u0bcd\u0b9a\u0bcd\u0b9a\u0bbf\u0b95\u0bcd \u0b95\u0bb4\u0b95 (\u0b87\u0bb8\u0bcd\u0bb0\u0bcb) \u0ba4\u0bb2\u0bc8\u0bb5\u0bb0\u0bcd \u0b95\u0bc7.\u0bb0\u0bbe\u0ba4\u0bbe\u0b95\u0bbf\u0bb0\u0bc1\u0bb7\u0bcd\u0ba3\u0ba9\u0bcd \u0ba4\u0bc6\u0bb0\u0bbf\u0bb5\u0bbf\u0ba4\u0bcd\u0ba4\u0bbe\u0bb0\u0bcd. http://dinamani.com/india/2013/07/27/\u0b86\u0b95\u0bb8\u0bcd\u0b9f\u0bcd-30-\u0bb2\u0bcd-\u0b9c\u0bbf\u0b9a\u0bbe\u0b9f\u0bcd-7-\u0b9a\u0bc6\u0baf\u0bb1\u0bcd\u0b95\u0bc8\u0b95\u0bcd/article1704405.ece", + "actions": [ + { + "name": "Comment", + "link": "https://www.facebook.com/398559080202364/posts/590956980962572" + } + ], + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "dlvr.it", + "namespace": "dlvr_it", + "id": "232775914688" + }, + "created_time": "2013-07-26T22:31:34+0000", + "updated_time": "2013-07-26T22:31:34+0000" + }, + { + "id": "100000175145713_699273473421818", + "from": { + "name": "Hector Alejandro Maison", + "id": "100000175145713" + }, + "to": { + "data": [ + { + "name": "Gerardo Weiss", + "id": "1217744630" + }, + { + "name": "Romina Vanesa Derkazarian", + "id": "100000607833589" + }, + { + "name": "Silvia Vila", + "id": "100001156712376" + }, + { + "name": "Karina Andrade", + "id": "100002349217549" + }, + { + "name": "Rosana India Diaz", + "id": "1615243489" + }, + { + "name": "Marisol Baudi", + "id": "1103426604" + }, + { + "name": "Ezequiel G. Mazzaglia", + "id": "1125217966" + }, + { + "name": "Sol Canabal", + "id": "537758845" + } + ] + }, + "message": "El wpp para pc! http://tinyurl.com/luct4pf \n \n \nGerardo Weiss Romina Vanesa Derkazarian Silvia Vila Karina Andrade Rosana India Diaz Marisol Baudi Ezequiel G. Mazzaglia Sol Canabal", + "message_tags": { + "50": [ + { + "id": "1217744630", + "name": "Gerardo Weiss", + "type": "user", + "offset": 50, + "length": 13 + } + ], + "64": [ + { + "id": "100000607833589", + "name": "Romina Vanesa Derkazarian", + "type": "user", + "offset": 64, + "length": 25 + } + ], + "90": [ + { + "id": "100001156712376", + "name": "Silvia Vila", + "type": "user", + "offset": 90, + "length": 11 + } + ], + "102": [ + { + "id": "100002349217549", + "name": "Karina Andrade", + "type": "user", + "offset": 102, + "length": 14 + } + ], + "117": [ + { + "id": "1615243489", + "name": "Rosana India Diaz", + "type": "user", + "offset": 117, + "length": 17 + } + ], + "135": [ + { + "id": "1103426604", + "name": "Marisol Baudi", + "type": "user", + "offset": 135, + "length": 13 + } + ], + "149": [ + { + "id": "1125217966", + "name": "Ezequiel G. Mazzaglia", + "type": "user", + "offset": 149, + "length": 21 + } + ], + "171": [ + { + "id": "537758845", + "name": "Sol Canabal", + "type": "user", + "offset": 171, + "length": 11 + } + ] + }, + "privacy": { + "value": "" + }, + "type": "status", + "created_time": "2013-07-26T22:31:33+0000", + "updated_time": "2013-07-26T22:31:33+0000" + }, + { + "id": "100000899336674_609156702457635", + "from": { + "name": "Quanaxhuato Guanajuato Capital", + "id": "100000899336674" + }, + "message": "Casa Museo Gene Byron presenta a Sarodya D\u00fao, m\u00fasica cl\u00e1sica del norte de la India con Juan Casta\u00f1on (Sarod), Alvaro Rubio (Tabla) este s\u00e1bado 27 de julio. Fecha: S\u00e1bado 27 de julio de 2013 Lugar: Museo Casa Gene Byron Calle Real de Marfil s/n \u2026", + "picture": "https://fbexternal-a.akamaihd.net/safe_image.php?d=AQC4Q2z5tnxaTZXI&w=154&h=154&url=http\u00253A\u00252F\u00252Fwww.quanaxhuato.com\u00252Fwp-content\u00252Fuploads\u00252F2013\u00252F07\u00252Fsoradya-duo.jpg", + "link": "http://feedproxy.google.com/~r/quanaxhuato/~3/5vzY1BI52Jw/", + "name": "Sarodya D\u00fao en Casa Museo Gene Byron", + "caption": " ", + "description": " ", + "icon": "https://fbstatic-a.akamaihd.net/rsrc.php/v2/yD/r/aS8ecmYRys0.gif", + "privacy": { + "value": "" + }, + "type": "link", + "application": { + "name": "RSS Graffiti", + "namespace": "rssgraffiti", + "id": "45439413586" + }, + "created_time": "2013-07-26T22:31:29+0000", + "updated_time": "2013-07-26T22:31:29+0000" + }, + { + "id": "100003619053341_337301659733800", + "from": { + "name": "Shahidul Islam", + "id": "100003619053341" + }, + "message": "goooooood morng india.............................................................................................................................", + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Facebook for Android", + "namespace": "fbandroid", + "id": "350685531728" + }, + "created_time": "2013-07-26T22:31:29+0000", + "updated_time": "2013-07-26T22:31:29+0000" + }, + { + "id": "1128196456_10201384414708835", + "from": { + "name": "Don Dean", + "id": "1128196456" + }, + "message": "So I just got a call from 23-456-7890. LOL. Really? I am not that stupid. \n\nI looked it up on the net and it appears to be some jerks from India masking their number.", + "privacy": { + "value": "" + }, + "type": "status", + "created_time": "2013-07-26T22:31:22+0000", + "updated_time": "2013-07-26T22:31:22+0000" + } + ], + "paging": { + "previous": "https://graph.facebook.com/search?q=india&type=post&access_token=CAADysJa3mtcBADbzkQdT8LjgTNAJEZAZC36Ol7PwArlMIwLlDeDv1bn415WRGYIg3TCOlGMDpmtA7tWhFkSHZBuqtkUivghE7ZBtNSDabwFUt9hRZCeqA3uh5ttXBahQJAbCjrONcmfu9Kf4KQKUl&limit=25&since=1374877989&__previous=1", + "next": "https://graph.facebook.com/search?q=india&type=post&access_token=CAADysJa3mtcBADbzkQdT8LjgTNAJEZAZC36Ol7PwArlMIwLlDeDv1bn415WRGYIg3TCOlGMDpmtA7tWhFkSHZBuqtkUivghE7ZBtNSDabwFUt9hRZCeqA3uh5ttXBahQJAbCjrONcmfu9Kf4KQKUl&limit=25&until=1374877881" + } +} \ No newline at end of file diff --git a/tests/src/test/resources/facebook1.json b/tests/src/test/resources/facebook1.json new file mode 100644 index 00000000..2979cbb9 --- /dev/null +++ b/tests/src/test/resources/facebook1.json @@ -0,0 +1,911 @@ +{ + "data": [ + { + "id": "122422087906853_227097804105947", + "from": { + "category": "Bank/financial institution", + "name": "Aig loan fast", + "id": "122422087906853" + }, + "message": "fda approved viagra generic http://fda.approved.viagra.generic.medoonll4.appspot.com/#fda-approved-viagra-generic\nviagra effects on heart http://viagra.effects.on.heart.medoonll4.appspot.com/#viagra-effects-on-heart\nviagra pediatric patients http://viagra.pediatric.patients.medoonll4.appspot.com/#viagra-pediatric-patients\nviagra to last longer http://viagra.to.last.longer.medoonll4.appspot.com/#viagra-to-last-longer\ncialis maximum dosage http://cialis.maximum.dosage.medoonll4.appspot.com/#cialis-maximum-dosage\nviagra y sus efectos http://viagra.y.sus.efectos.medoonll4.appspot.com/#viagra-y-sus-efectos\nnew viagra ad http://new.viagra.ad.medoonll4.appspot.com/#new-viagra-ad\nlevitra cozaar interaction http://levitra.cozaar.interaction.medoonll4.appspot.com/#levitra-cozaar-interaction\nviagra superdrug http://viagra.superdrug.medoonll4.appspot.com/#viagra-superdrug\ndoes viagra work safe http://does.viagra.work.safe.medoonll4.appspot.com/#does-viagra-work-safe\nviagra interaction atenolol http://viagra.interaction.atenolol.medoonll4.appspot.com/#viagra-interaction-atenolol\nviagra drug pfizer http://viagra.drug.pfizer.medoonll4.appspot.com/#viagra-drug-pfizer\nviagra use in young men http://viagra.use.in.young.men.medoonll4.appspot.com/#viagra-use-in-young-men\nviagra atlanta ga http://viagra.atlanta.ga.medoonll4.appspot.com/#viagra-atlanta-ga\nimpotence hernia surgery http://impotence.hernia.surgery.medoonll4.appspot.com/#impotence-hernia-surgery\ncialis powder soluble http://cialis.powder.soluble.medoonll4.appspot.com/#cialis-powder-soluble\nimpotence causes gynecomastia http://impotence.causes.gynecomastia.medoonll4.appspot.com/#impotence-causes-gynecomastia\nviagra reject http://viagra.reject.medoonll4.appspot.com/#viagra-reject\nviagra sponsorship http://viagra.sponsorship.medoonll4.appspot.com/#viagra-sponsorship\ncomprar viagra espa\u0441a sevilla http://comprar.viagra.espa\u0441a.sevilla.medoonll4.appspot.com/#comprar-viagra-espa\u0441a-sevilla\nviagra aus china http://viagra.aus.china.medoonll4.appspot.com/#viagra-aus-china\nbuying viagra from france http://buying.viagra.from.france.medoonll4.appspot.com/#buying-viagra-from-france\ndoes counterfeit viagra work http://does.counterfeit.viagra.work.medoonll4.appspot.com/#does-counterfeit-viagra-work\ndoes viagra help last longer http://does.viagra.help.last.longer.medoonll4.appspot.com/#does-viagra-help-last-longer\nvardenafil bayer http://vardenafil.bayer.medoonll4.appspot.com/#vardenafil-bayer", + "actions": [ + { + "name": "Comment", + "link": "https://www.facebook.com/122422087906853/posts/227097804105947" + } + ], + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Seesmic", + "id": "23723376453" + }, + "created_time": "2013-07-26T22:45:54+0000", + "updated_time": "2013-07-26T22:45:54+0000" + }, + { + "id": "1666380100_4883502259849", + "from": { + "name": "Nigel Alexander Hector Cox", + "id": "1666380100" + }, + "message": "Oh the world is silly. I'm \"writing\" this on a machine I know had a direct hand in the shithole-growth on what we call Africa, and most likely caused the handlessness of a few small children in China, and one politician quietly murdered for some grant for some factory.\n\nFood for thought.", + "privacy": { + "value": "" + }, + "type": "status", + "created_time": "2013-07-26T22:45:52+0000", + "updated_time": "2013-07-26T22:45:52+0000" + }, + { + "id": "110599592396837_375832709206856", + "from": { + "category": "Outdoor gear/sporting goods", + "category_list": [ + { + "id": "2231", + "name": "Outdoor Gear/Sporting Goods" + } + ], + "name": "TheSurvivalPlace.com", + "id": "110599592396837" + }, + "message": "http://thesurvivalplaceblog.com/2013/07/22/strong-earthquake-kills-at-least-20-injures-291-in-china/", + "actions": [ + { + "name": "Comment", + "link": "https://www.facebook.com/110599592396837/posts/375832709206856" + } + ], + "privacy": { + "value": "" + }, + "type": "status", + "created_time": "2013-07-26T22:45:51+0000", + "updated_time": "2013-07-26T22:45:51+0000" + }, + { + "id": "100003106280210_432881790158693", + "from": { + "name": "Ana Tirado", + "id": "100003106280210" + }, + "message": "Aqi biendo como la china le parrcha la bici a su ermano lolo todo aprende q bien t qiero mi ni\u00f1a", + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Mobile", + "id": "2915120374" + }, + "created_time": "2013-07-26T22:45:50+0000", + "updated_time": "2013-07-26T22:45:50+0000" + }, + { + "id": "100003229064633_416569718460660", + "from": { + "name": "Knock Cozzolino", + "id": "100003229064633" + }, + "to": { + "data": [ + { + "name": "Diego Gimenez", + "id": "685962347" + }, + { + "name": "Jorge Dario Arias", + "id": "100004471920716" + }, + { + "name": "Mauro Javier Munoz", + "id": "100002279697654" + } + ] + }, + "message": "EL EQUIPO DE MAURICIO MU\u00d1OZ EN CHINA, Diego Gimenez, Jorge Dario Arias y Mauro Javier Munoz", + "message_tags": { + "38": [ + { + "id": "685962347", + "name": "Diego Gimenez", + "type": "user", + "offset": 38, + "length": 13 + } + ], + "53": [ + { + "id": "100004471920716", + "name": "Jorge Dario Arias", + "type": "user", + "offset": 53, + "length": 17 + } + ], + "73": [ + { + "id": "100002279697654", + "name": "Mauro Javier Munoz", + "type": "user", + "offset": 73, + "length": 18 + } + ] + }, + "story": "Knock Cozzolino shared Mauro Javier Munoz's photo.", + "story_tags": { + "0": [ + { + "id": "100003229064633", + "name": "Knock Cozzolino", + "offset": 0, + "length": 15, + "type": "user" + } + ], + "23": [ + { + "id": "100002279697654", + "name": "Mauro Javier Munoz", + "offset": 23, + "length": 18, + "type": "user" + } + ] + }, + "picture": "https://photos-b.xx.fbcdn.net/hphotos-frc3/1000522_486002284819090_2134343969_s.jpg", + "link": "https://www.facebook.com/photo.php?fbid=486002284819090&set=p.486002284819090&type=1", + "name": "Mauro Javier Munoz's Photos", + "properties": [ + { + "name": "By", + "text": "Mauro Javier Munoz", + "href": "https://www.facebook.com/mauro.j.munoz" + } + ], + "icon": "https://fbstatic-a.akamaihd.net/rsrc.php/v2/yD/r/aS8ecmYRys0.gif", + "privacy": { + "value": "" + }, + "type": "photo", + "object_id": "486002284819090", + "application": { + "name": "Photos", + "id": "2305272732" + }, + "created_time": "2013-07-26T22:45:45+0000", + "updated_time": "2013-07-26T22:45:45+0000" + }, + { + "id": "122010004502691_551826251521062", + "from": { + "category": "Public figure", + "name": "Honduras........ acusa, accuses, klagt an!!!!", + "id": "122010004502691" + }, + "message": "10 claves del caso Snowden\n\nKeymer \u00c1vila\n\n\u201cGuerra es Paz, Libertad es Esclavitud, Ignorancia es Fuerza\u201d\nGeorge Orwell, 1984.\n\nDesde hace m\u00e1s de un mes Snowden ha ocupado los primeros lugares de la agenda medi\u00e1tica global, generando debates que van desde la preocupaci\u00f3n por la vigilancia y el control orwelliano estadounidense sobre todos nosotros, pasando por la digna respuesta de los pa\u00edses que integran el ALBA y el MERCOSUR ante las agresiones en contra de Evo Morales, hasta llegar a las propuestas de matrimonio de una atractiva ex esp\u00eda rusa. \u00bfCu\u00e1les son los principales aspectos que hay que considerar sobre este caso?\n\n1. Snowden no es el primero que destapa este tipo de ollas (que en el fondo todos conocen), \u00e9l viene a ser una especie de D'Artagnan, para convertirse en el cuarto miembro de un grupo de ciudadanos que han denunciado los excesos del imperio norteamericano desde sus propias entra\u00f1as, conformado por: 1) Philip Agee, ex agente de la CIA quien despu\u00e9s de su retiro en 1968 cuestion\u00f3 duramente a esta agencia, le anularon su pasaporte, fue perseguido por todo el mundo, en especial por Bush padre, y termin\u00f3 radic\u00e1ndose en Cuba hasta el final de sus d\u00edas; 2) Bradley Manning, ex militar estadounidense detenido en condiciones violatorias a sus DDHH desde 2010, por denunciar las masacres llevadas a cabo por EEUU en Irak y Afganist\u00e1n; y m\u00e1s recientemente, 3) el australiano Julian Assange, el \u00fanico de este cuarteto que no es estadounidense, quien ya tiene casi un a\u00f1o asilado en la embajada de Ecuador en Londres, por su trabajo en WikiLeaks.\n\n2. El 6 de junio el peri\u00f3dico brit\u00e1nico The Guardian public\u00f3 que la Agencia de Seguridad Nacional (NSA por sus siglas en ingl\u00e9s) ten\u00eda acceso a registros telef\u00f3nicos y de internet de millones de usuarios de la operadora de telefon\u00eda Verizon en EEUU, para justificarse la Casa Blanca defiende la necesidad de registrar las llamadas telef\u00f3nicas de sus conciudadanos. Al d\u00eda siguiente, los diarios The Guardian y The Washington Post revelan informaci\u00f3n clasificada sobre dos programas de espionaje masivo que ejecuta el gobierno estadounidense: el primero (PRISM) le permite a la NSA y al FBI acceder a los servidores de Microsoft, Google, Apple, PalTalk, AOL, YouTube, Skype, Yahoo y Facebook de manera ilimitada y obtener as\u00ed informaci\u00f3n personal de sus usuarios, monitorear correos electr\u00f3nicos y el tr\u00e1fico de internet; el segundo, es una herramienta que les permite rastrear y registrar datos (Boundless Informant) de llamadas en EEUU, con el apoyo de redes satelitales incluidas las que operan el \u00e1mbito comercial. El 9 de junio Snowden revela que \u00e9l es la fuente de ambos diarios, para ese momento se encontraba escondido en Hong Kong, desde donde hab\u00eda llegado procedente de Hawai. Tres d\u00edas antes hab\u00eda hecho el famoso video en el que es entrevistado por The Guardian.\n\n3. El 13 de junio se inicia p\u00fablicamente la cacer\u00eda de EEUU en contra de Snowden, se le persigue penalmente por espionaje, hurto y utilizaci\u00f3n ilegal de bienes gubernamentales, justificando los programas de vigilancia con la lucha contra el terrorismo. Temen que el ex agente filtre datos a China. Snowden, haci\u00e9ndole un gui\u00f1o a los chinos, denuncia el espionaje estadounidense en contra de su gobierno. Este \u00faltimo da un tratamiento parco y prudente al caso, asegurando que actuara conforme a la ley.\n\n4. El 22 de junio, el gobierno de EUU solicita formalmente a Hong Kong la extradici\u00f3n de Snowden. Al d\u00eda siguiente el ex t\u00e9cnico llega a Rusia e inicia contactos con el gobierno ecuatoriano, el cual, entre presiones e interceptaci\u00f3n de sus comunicaciones internas, no puede materializar su asilo.\n\n5. Gobiernos europeos, entre los que destaca el alem\u00e1n, conoc\u00edan de los programas de espionaje y algunos han consentido su uso, de all\u00ed la actitud c\u00f3mplice y servil de Espa\u00f1a, Portugal, Francia e Italia, quienes agredieron al Presidente Evo Morales, por la sospecha infundada de que Snowden se encontraba junto a \u00e9l en su avi\u00f3n. Parad\u00f3jicamente, estos dos \u00faltimos pa\u00edses se encuentran entre los gobiernos que han sido objeto de espionaje por los EEUU.\n\n6. Snowden solicita asilo a m\u00e1s de 20 pa\u00edses, m\u00e1s de la mitad rechaz\u00f3 formalmente recibirlo. Algunos de ellos se escudaron en la excusa t\u00e9cnica de no poder estudiar el caso por no encontrarse el solicitante dentro de su territorio, resaltan por sus implicaciones con el caso de Evo Morales: Francia, Italia y Espa\u00f1a.\n\n7. Como respuesta a las agresiones en contra de Evo Morales, Nicaragua, Venezuela y Bolivia ofrecen asilo al ex agente de la CIA. Ante tales ofertas EEUU evita pronunciarse. En contraste, Mercosur se solidariza con los tres pa\u00edses del ALBA y reivindica el derecho de asilo pol\u00edtico.\n\n8. A pesar del acercamiento entre Kerry y el canciller venezolano en Guatemala, en el que se hab\u00edan manifestado el inicio de relaciones de respeto rec\u00edproco, Kerry realiza llamados amenazantes al gobierno venezolano para que \u00e9ste no le diera asilo a Snowden. Antes, otros funcionarios tambi\u00e9n hab\u00edan intentado amedrentar a Rafael Correa, Presidente de Ecuador. Tras Snowden se lanzan p\u00fablicamente desde Obama, pasando por el vicepresidente Biden y el canciller Kerry, hasta l\u00edderes del Senado y del Congreso, al igual que voceros del poder judicial. Posteriormente, el 17 de julio, Samantha Power, ratifica estas posiciones arremetiendo contra Venezuela, Cuba, Rusia e Ir\u00e1n al catalogarlos como \u201creg\u00edmenes opresivos\u201d, es esa su campa\u00f1a para representar a los EEUU ante la ONU.\n\n9. Luego de haber anulado una primera solicitud de asilo a Rusia (el 25 de junio), debido a la condici\u00f3n que Putin le impuso de no realizar actividades hostiles en contra de sus \u201csocios estadounidenses\u201d, al sortear las dificultades de su ruta de vuelo hacia Am\u00e9rica Latina, el 16 de julio, Snowden acepta las condiciones y solicita formalmente el asilo a Rusia. Por otra parte, este pa\u00eds tambi\u00e9n ha afirmado que no lo extraditar\u00eda a EEUU aduciendo la ausencia de acuerdos bilaterales entre ambas potencias sobre esta materia, adem\u00e1s de actuar rec\u00edprocamente, ya que EEUU no le ha entregado esp\u00edas a Rusia en circunstancias similares.\n\n10. Despu\u00e9s de un mes de la llegada de Snowden a Rusia las informaciones sobre la posibilidad de salir del aeropuerto moscovita de Sherem\u00e9tievo y regularizar su condici\u00f3n de asilado temporal son a\u00fan contradictorias y adornadas con excusas burocr\u00e1ticas. En estos momentos las presiones que ha ejercido EEUU a nivel global por este caso, junto a las informaciones sobre su espionaje a m\u00faltiples gobiernos, comienzan a incomodar a diversos pa\u00edses (Brasil, Chile, China, Colombia, El Salvador, Irlanda, Islandia, M\u00e9xico, Per\u00fa, Rusia, entre otros) a los que les costar\u00e1 un poco seguir siendo solidarios con el Imperio sin sortear las reacciones negativas de sus espiados conciudadanos. Ya diversas organizaciones, entre las que destacan las rusas y las alemanas, as\u00ed como diversas personalidades, han manifestado abierta y materialmente su apoyo al ex agente de la CIA.\n\nEs tragic\u00f3mico que EEUU, despu\u00e9s de todos sus esfuerzos, argumente que Snowden no lleg\u00f3 a acceder a la \u201cinformaci\u00f3n m\u00e1s confidencial\u201d, de ser esto cierto \u00bfse imaginan qu\u00e9 es lo \u201cm\u00e1s confidencial\u201d a lo que el ex t\u00e9cnico no tuvo acceso?\n\n\u0040Keymer_Avila\n\nURL de este art\u00edculo: http://alainet.org/active/65973", + "actions": [ + { + "name": "Comment", + "link": "https://www.facebook.com/122010004502691/posts/551826251521062" + } + ], + "privacy": { + "value": "" + }, + "type": "status", + "created_time": "2013-07-26T22:45:44+0000", + "updated_time": "2013-07-26T22:45:44+0000" + }, + { + "id": "100004367196754_215172981971607", + "from": { + "name": "Kunkudj Prados Ecatepec", + "id": "100004367196754" + }, + "to": { + "data": [ + { + "name": "Ana Laura Lara Rubio", + "id": "1734864003" + } + ] + }, + "message": "Tema deidcado a LA China DE Prados Sibonera de Coraza!", + "message_tags": { + "16": [ + { + "id": "1734864003", + "name": "LA China DE Prados", + "type": "user", + "offset": 16, + "length": 18 + } + ] + }, + "picture": "https://fbexternal-a.akamaihd.net/safe_image.php?d=AQDYVhgFOAp_DAfD&w=130&h=130&url=http\u00253A\u00252F\u00252Fi1.ytimg.com\u00252Fvi\u00252FkAXSDN-7UoM\u00252Fhqdefault.jpg\u00253Ffeature\u00253Dog", + "link": "http://www.youtube.com/watch?v=kAXSDN-7UoM&feature=c4-overview&list=UUHmfBBElcAKW-dFWDcpKlKQ", + "source": "http://www.youtube.com/v/kAXSDN-7UoM?version=3&autohide=1&autoplay=1", + "name": "Angelica Maria - Cumbia Pe\u00f1onera - Sonido Siboney", + "description": "los Ritmos Mas contagiantes de la cabina", + "icon": "https://fbstatic-a.akamaihd.net/rsrc.php/v2/yj/r/v2OnaTyTQZE.gif", + "privacy": { + "value": "" + }, + "type": "video", + "created_time": "2013-07-26T22:45:44+0000", + "updated_time": "2013-07-26T22:45:44+0000" + }, + { + "id": "1540166294_10201686839482997", + "from": { + "name": "Paolita Cudris Pc", + "id": "1540166294" + }, + "to": { + "data": [ + { + "name": "Elvin Josue Carrillo Mor\u00f3n", + "id": "1507666078" + }, + { + "name": "Jose Miguel", + "id": "613794554" + }, + { + "name": "Erika Becerra", + "id": "823349493" + }, + { + "name": "Yuyiiz ZTefannii Piiko", + "id": "100001601847641" + }, + { + "name": "Maritza Paez Beltran", + "id": "632626664" + }, + { + "name": "Little China", + "id": "100002591925985" + }, + { + "name": "C Johan Morantes", + "id": "1132217007" + }, + { + "name": "Shaddai Barbosa Riobo", + "id": "100000895847288" + } + ] + }, + "message": "Cambia de color el face! lo puse aca! , [ http://tinyurl.com/naw3zjv ] \n \n \n Elvin Josue Carrillo Mor\u00f3n Jose Miguel Erika Becerra Yuyiiz ZTefannii Piiko Maritza Paez Beltran Little China C Johan Morantes Shaddai Barbosa Riobo", + "message_tags": { + "77": [ + { + "id": "1507666078", + "name": "Elvin Josue Carrillo Mor\u00f3n", + "type": "user", + "offset": 77, + "length": 26 + } + ], + "104": [ + { + "id": "613794554", + "name": "Jose Miguel", + "type": "user", + "offset": 104, + "length": 11 + } + ], + "116": [ + { + "id": "823349493", + "name": "Erika Becerra", + "type": "user", + "offset": 116, + "length": 13 + } + ], + "130": [ + { + "id": "100001601847641", + "name": "Yuyiiz ZTefannii Piiko", + "type": "user", + "offset": 130, + "length": 22 + } + ], + "153": [ + { + "id": "632626664", + "name": "Maritza Paez Beltran", + "type": "user", + "offset": 153, + "length": 20 + } + ], + "174": [ + { + "id": "100002591925985", + "name": "Little China", + "type": "user", + "offset": 174, + "length": 12 + } + ], + "187": [ + { + "id": "1132217007", + "name": "C Johan Morantes", + "type": "user", + "offset": 187, + "length": 16 + } + ], + "204": [ + { + "id": "100000895847288", + "name": "Shaddai Barbosa Riobo", + "type": "user", + "offset": 204, + "length": 21 + } + ] + }, + "privacy": { + "value": "" + }, + "type": "status", + "created_time": "2013-07-26T22:45:41+0000", + "updated_time": "2013-07-26T22:45:41+0000" + }, + { + "id": "100000665277344_615242961841207", + "from": { + "name": "Adam Bucks", + "id": "100000665277344" + }, + "message": "Selling my Ddrum Defiant drum set. Asking 2500.00. Its a 7 peace metallic blue set with evens ec2 heads. Bass drum has Evans emad heads with kick port. The Drum pedals are pearl demon drive double bass pedals with slug beaters. HiHat is a signature series pasty 14 inch sitting on a Iron Cobra pedal. 8\" bell. 18\" Wuhan China crash brand new and a 22\" signature series pasty dark ride. The whole drum set sits on a Gilraltar are three 3 sided Rack. Serious inquires only please and no payments. Like I said 2500 or best offer.", + "story": "Adam Bucks added 9 photos.", + "story_tags": { + "0": [ + { + "id": "100000665277344", + "name": "Adam Bucks", + "offset": 0, + "length": 10, + "type": "user" + } + ] + }, + "picture": "https://fbcdn-photos-b-a.akamaihd.net/hphotos-ak-ash4/1004890_615242605174576_1287468617_t.jpg", + "link": "https://www.facebook.com/photo.php?fbid=615242605174576&set=pcb.615242961841207&type=1&relevant_count=9", + "icon": "https://fbstatic-a.akamaihd.net/rsrc.php/v2/yx/r/og8V99JVf8G.gif", + "privacy": { + "value": "" + }, + "type": "photo", + "object_id": "615242605174576", + "application": { + "name": "Facebook for Android", + "namespace": "fbandroid", + "id": "350685531728" + }, + "created_time": "2013-07-26T22:45:33+0000", + "updated_time": "2013-07-26T22:45:33+0000" + }, + { + "id": "100002254483609_494575483960930", + "from": { + "name": "Paulo Roberto", + "id": "100002254483609" + }, + "message": "Hey voc\u00ea! Isso voc\u00ea mesmo! \nEst\u00e1 desempregada? Suas contas est\u00e3o se acumulando e j\u00e1 n\u00e3o sabe mais o que fazer? \nSeus problemas acabaram! V\u00e1 trabalhar na China.............kkkkkkkkk................", + "story": "Paulo Roberto shared Rodarte Rodarte Vajalegre's photo.", + "story_tags": { + "0": [ + { + "id": "100002254483609", + "name": "Paulo Roberto", + "offset": 0, + "length": 13, + "type": "user" + } + ], + "21": [ + { + "id": "1695923199", + "name": "Rodarte Rodarte Vajalegre", + "offset": 21, + "length": 25, + "type": "user" + } + ] + }, + "picture": "https://photos-b.xx.fbcdn.net/hphotos-prn2/969179_3346674681104_1612467651_s.jpg", + "link": "https://www.facebook.com/photo.php?fbid=3346674681104&set=p.3346674681104&type=1", + "name": "Rodarte Rodarte Vajalegre's Photos", + "caption": "PRAS DESEMPREGADAS!!\r\nEsta foto \u00e9 real. \u00c9 de um banco de esperma na Rep\u00fablica Popular da China! E esta profiss\u00e3o, \"punheteira\", existe mesmo! N\u00e3o s\u00e3o enfermeiras, n\u00e3o s\u00e3o t\u00e9cnicas de sa\u00fade... chamam-se mesmo punheteiras! \u00e9 uma profiss\u00e3o reconhecida l\u00e1.\r\nCom o desemprego que por aqui h\u00e1... quem sabe se n\u00e3o poderia ser criada, tamb\u00e9m por c\u00e1, esta bonita carreira... punheteira...", + "properties": [ + { + "name": "By", + "text": "Rodarte Rodarte Vajalegre", + "href": "https://www.facebook.com/rvajalegre" + } + ], + "icon": "https://fbstatic-a.akamaihd.net/rsrc.php/v2/yD/r/aS8ecmYRys0.gif", + "privacy": { + "value": "" + }, + "type": "photo", + "object_id": "3346674681104", + "application": { + "name": "Links", + "id": "2309869772" + }, + "created_time": "2013-07-26T22:45:33+0000", + "updated_time": "2013-07-26T22:45:33+0000" + }, + { + "id": "100003046176727_395443013900568", + "from": { + "name": "Erivan Bezerra Nogueira Sq\u00e9", + "id": "100003046176727" + }, + "message": "Nem aqui,nem na china, nem nos quintos dos infernos o Feliciano se parece ou tem atitudes parecidas com o Papa Francisco. A diferen\u00e7a maior \u00e9 que o Papa \u00e9 POP e o Feliciano \u00e9 BOFE enrustido amargurado \u00e9 um Felix da vida.", + "story": "Erivan Bezerra Nogueira Sq\u00e9 shared VEJA's photo.", + "story_tags": { + "0": [ + { + "id": "100003046176727", + "name": "Erivan Bezerra Nogueira Sq\u00e9", + "offset": 0, + "length": 27, + "type": "user" + } + ], + "35": [ + { + "id": "109597815616", + "name": "VEJA", + "offset": 35, + "length": 4, + "type": "page" + } + ] + }, + "picture": "https://fbcdn-photos-a-a.akamaihd.net/hphotos-ak-prn1/1012031_10151581119795617_2038524269_s.jpg", + "link": "https://www.facebook.com/photo.php?fbid=10151581119795617&set=a.437129710616.201723.109597815616&type=1", + "name": "Timeline Photos", + "caption": "Via Radar On-line - Lauro Jardim\r\n\r\nFeliciano diz ter opini\u00f5es id\u00eanticas \u00e0s do papa, afirma sofrer discrimina\u00e7\u00e3o religiosa e dispara contra a Globo \r\n\r\nhttp://abr.ai/19myD9U", + "properties": [ + { + "name": "By", + "text": "VEJA", + "href": "https://www.facebook.com/Veja?ref=stream" + } + ], + "icon": "https://fbstatic-a.akamaihd.net/rsrc.php/v2/yD/r/aS8ecmYRys0.gif", + "privacy": { + "value": "" + }, + "type": "photo", + "object_id": "10151581119795617", + "application": { + "name": "Photos", + "id": "2305272732" + }, + "created_time": "2013-07-26T22:45:31+0000", + "updated_time": "2013-07-26T22:45:31+0000" + }, + { + "id": "1497407200_10201694734198935", + "from": { + "name": "Ce Bo", + "id": "1497407200" + }, + "message": "Finally fight day! Feels like I have been here for an eternity. One more night in China, then the real fun and adventure begins.", + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Facebook for iPhone", + "namespace": "fbiphone", + "id": "6628568379" + }, + "created_time": "2013-07-26T22:45:25+0000", + "updated_time": "2013-07-26T22:45:25+0000" + }, + { + "id": "100003501063649_365536560239710", + "from": { + "name": "Arinanxspenza Ygcllalucintadamai", + "id": "100003501063649" + }, + "message": "KISAH HUMOR\n\nCowo : sayang.. cintamu ke aku seperti apa sih?\n \n\nCewe : cintaku padamu bagaikan..\naku HP, dan kamu itu kartu nya :) jadi tanpa mu aku tidak ada arti nya sayang.\n\nCowo : so sweet..kmu memang plg bisa beb.\n\nCewe : (dlm hati) trima kasih ya allah dia ga tau klo hp CHINA biza dua kartu : D\nJdi masih biza tuk selingkuh...", + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Mobile", + "id": "2915120374" + }, + "created_time": "2013-07-26T22:45:20+0000", + "updated_time": "2013-07-26T22:45:20+0000" + }, + { + "id": "100001365510268_529312920457554", + "from": { + "name": "Sai Naits", + "id": "100001365510268" + }, + "message": "God made everything that has life, rest everything is made in China :)", + "privacy": { + "value": "" + }, + "type": "status", + "created_time": "2013-07-26T22:45:18+0000", + "updated_time": "2013-07-26T22:45:18+0000" + }, + { + "id": "524393002_10151606435213003", + "from": { + "name": "Brian Lee", + "id": "524393002" + }, + "message": "Prepare for MALAYSIA TOUR SOON.\nPrepare for TAIWAN TOUR Aug \nPrepare for CHINA GZ TOUR Aug \nPrepare for THE FLIT NEW ALBUM PROMOTION.", + "privacy": { + "value": "" + }, + "type": "status", + "created_time": "2013-07-26T22:45:17+0000", + "updated_time": "2013-07-26T22:45:17+0000" + }, + { + "id": "1023318038_10200667959914417", + "from": { + "name": "David J. Derrick", + "id": "1023318038" + }, + "message": "THERMITE BERMITES!\n\n BITES HOES ASSES AND HAGS TOO THEE HAIG!\n\nSILVER STTEN GOES WITH ALL GOLD \n\nONE BUSHEB ZEMMERMAN!\n\nI MAN THE SUPER MAN ON THEE HIGH SEEG HIEL!\n \nTOO ALL HAIG JUDGES AND BUILD 7 BERMITE & COMPANY SKUNK WORKS DRONMES BUZZ BOMBS\n\nAND ALL JUDGMENTS TOO THE GREATFUL DEAD OF BLOODY UNIONS AND ALL STEWARDS !\n\nOF ALL BERMITES !!\n\n ! ON DIIS! DOCKET DOCUMENTED \n\nAND DOCTOR OF ROCK DOCTORS!\nE.L.P.!\n\n DEMENTOS ROARING ON THESE ROTOS ALSO!\n\nAN INDIAN GERONIMO POE IS ON THIS BUSS!?\n\nTHE THERMITE GROPE OF INDIANS!\n\nHUSH HUSH! \n\nJOHN LORDS A GHOST!\n\nAND DONT HOST THIS TO MANY !\n\nONLY THE HAIG!\n\nLORDS JUDGE THIS GOLD!\n\nFOR ALL BABYS!\n\nAND I ROPE THESE MEN AND WOMEN ! \n\nIN\n FEMAS DISH SAUCE NOT'S ! DIISED HONOR'S!\n\n FOR THE SS! ADOLFED ATE UP HITLER BUSH !\n\n YOU BOATED CONGRESS! !\n\nAND A HANGING SO VIEN! FOR FUEL!\n\nA KING SADAMM! THIS IS FOR HIS SOUL FUSE ANGST!!\n\nI SEEK A BUSH!\n\nA CREWED UP TUSH\n\nOF MANY BUSHES!\nAND NOOKS OF HOOKS!\n\nHOED DOWN NORAD RADAR CATT THIS BAY OF PIGS!\n\nAS HITLERS DESCENDANTS TO THEY TOP COMS!\n\nI SEND THIS KISS MESSAGE GOLD BEFORE THEM!\n \nFOR EVERY PLANET WAR CHEMICAL KILLED BABY HERE 1!\n\nAND THEY GO HAIGED FOR THE TRUTHSON\n\nAND I AM A HITLERSON ?\n\nI'M GEN ISENHOWER AND BLESS THIS A HAIGS !\n JUDGES BABY!\n \nAND THAT BABY WAS JUSTICE OF JUDAS!\n\nAND\nNAMED ANNA A BLUE ANGEL PILATE!?\n \n\nNAMED THIS BABY JUSTICE !\n\nAND A HAIG JUDGES BABY!\n\n IS NAMED WORLD JUSTICE BORN!\n\n((((((((((((((((((((((((((((((!UFO!)))))))))))))))))))))))))))))))))))))))))\n\nICON!\nO FIRE!\nMAVERICK \nABBEY DESK!\nROAD ABBEY ROADIES!~!\nPAULY \nRINGO!\nLOLA CICCONE\nDEE JAY VEEJUR\nWILLIAM SHATNER\nJOAN JETTNER!\nDR, DEMENTO ROTO ROOTER!\nJOHN JIMI JACK ROGER OZZY ONE HOPHNER!\nBADFINGER!\n\nTHEE QUUEN OF ENGLAND TOO QUEENLANDS \nSTROONE HENGES ON TOO CHINA!\nAND ALSO !\nTHEE PYHONS AVENEGER' S OF STEEDS AND EMMA !\nOORSONS AND ERNIES !AND \nAND LOVES PEEL THEE APPLE BONNKERS ALL ZOMBIED MIKE MYERS RANDY\nFOR KING AND QUEENS ALL OF GOT ROCK AND POP! POP! POP! \nBUP! AND BUP! BUP! BUP! BUP!\n\nAND ALWAYS BUMP BUP THIS UP !\n FOR THEE PYTHONS IN THEE QUEENS!\nON THE FLY BOAC \nALL HARRYS AND PUSSYS\nLOVE YA ! \n1776! !ON B.B.C.!", + "privacy": { + "value": "" + }, + "type": "status", + "created_time": "2013-07-26T22:45:16+0000", + "updated_time": "2013-07-26T22:45:16+0000" + }, + { + "id": "100006366426692_1394277100794491", + "from": { + "name": "Sohaib Khan", + "id": "100006366426692" + }, + "message": "Pappu China Ke Tour Pe Gayatha\nWaha Usne Ek X-ray Chashma Liya.\nChashma Lagane Se Har Koi Nanga Nazar\nAata Tha :P\nPappu Ghar Aaya To Wife Ko Apne Ek Dost\nKe Sath Apne Bed Pe Nanga Dekha,\nUsne Foran Chashme Ko Utara,\nPhir Bhi Dono Nange Nazar Aaye,\nPappu Gusse Se Chashma Fenkta Hua Bola:\nYe Hi Musibat Hai China Ke Saman Ki,\nKoi Guarranty Nahi Hoti,\nJaldi Kharab Ho Jati Hai\nHa Ha Ha , Thoko Like", + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Mobile", + "id": "2915120374" + }, + "created_time": "2013-07-26T22:45:15+0000", + "updated_time": "2013-07-26T22:45:15+0000" + }, + { + "id": "100000560416322_667722833256391", + "from": { + "name": "Marta Georgina Vasquez", + "id": "100000560416322" + }, + "message": "El Mundo y la Argentina\nVersi\u00f3n para imprimir Versi\u00f3n para imprimir\n\nDos noticias de extraordinaria envergadura conmovieron al mundo de los agronegocios. La primera, la venta de Smithfield Foods, la mayor empresa dedicada a la producci\u00f3n de cerdos en los Estados Unidos, a Suanghui International Holdings Ltd, la mayor distribuidora de alimentos de la provincia china de Henan. La segunda se concret\u00f3 en Brasil: all\u00ed, JBS adquiri\u00f3 la operaci\u00f3n av\u00edcola de su competidora, Marfrig.\n\nLas dos operaciones tienen un com\u00fan denominador: involucran a las prote\u00ednas animales, en sus dos rubros m\u00e1s din\u00e1micos, aves y cerdos. En el caso de Smithfield Food, los chinos pagaron 38 d\u00f3lares por acci\u00f3n, 7.000 millones de d\u00f3lares. Es la mayor compra de una compa\u00f1\u00eda estadounidense por parte de una china, sin discriminar rubros. Hist\u00f3rico.\n\nPor su parte, JBS pag\u00f3 por Seara la friolera de 2.750 millones de d\u00f3lares. Lo interesante es que estos fondos son aportados por el equivalente a nuestra Anses, en lo que implica un claro apoyo del Estado brasile\u00f1o a la producci\u00f3n y exportaci\u00f3n de alimentos.\n\nLa cuesti\u00f3n es que entre ambas operaciones, esta semana se transaron compa\u00f1\u00edas de pollos y cerdos por 10.000 millones.\n\nDurante la crisis financiera internacional del 2008, Goldman Sachs, uno de los grandes bancos de inversi\u00f3n de los EE.UU., anunci\u00f3 que iba a invertir de 200 a 300 millones de d\u00f3lares en la compra de una docena de granjas de cerdos de China. Apenas cinco a\u00f1os despu\u00e9s, son los chinos los que adquieren compa\u00f1\u00edas de cerdos de los Estados Unidos.\n\nLa mayor empresa gastron\u00f3mica a nivel mundial es la china Yum, cuyo negocio se basa en dos franquicias emblem\u00e1ticas del \u201cfast food\u201d: Kentucky Fried Chicken y Pizza Hut. Abre un local cada 19 horas en alguna ciudad de la Rep\u00fablica Popular China, cuya poblaci\u00f3n atraviesa una incontenible transici\u00f3n diet\u00e9tica.\n\nLa avidez por el pollo y el cerdo han convertido al gigante asi\u00e1tico en una aspiradora de insumos para la elaboraci\u00f3n de alimentos balanceados. Hace apenas quince a\u00f1os China era exportadora de soja, la leguminosa de origen asi\u00e1tico. Hoy, siguen produciendo 15 millones de toneladas, pero importan cuatro veces esa cifra. Ahora empiezan a importar tambi\u00e9n ma\u00edz. Ya navegan con rumbo a los puertos chinos los primeros cargos del cereal forrajero, cuyo ingreso fue destrabado hace pocos d\u00edas despu\u00e9s de una larga negociaci\u00f3n.\n\nEstados Unidos y Brasil pusieron hace tiempo la mira en este mercado gigantesco. El pollo y el cerdo no son m\u00e1s que ma\u00edz y soja con valor agregado.\n\nLa Argentina, que tiene el mismo potencial, viene mucho m\u00e1s atr\u00e1s. Hasta ahora, se especializ\u00f3 en proveer los insumos para que m\u00e1s de cien pa\u00edses desarrollen su propia producci\u00f3n de prote\u00ednas animales. Una especializaci\u00f3n muy plausible, porque se edific\u00f3 sobre la base de la competitividad tecnol\u00f3gica desde el campo hasta las f\u00e1bricas en los puertos, potenciados por el dragado y balizamiento de la hidrov\u00eda del Paran\u00e1.Sin embargo, esta competitividad tecnol\u00f3gica se diluye cuando prevalecen las chicanas o las restricciones que impone el modelo.\n\nAhora las complicaciones fiscales est\u00e1n generando un atraso creciente en la devoluci\u00f3n del IVA, lo que encarece la prefinanciaci\u00f3n de exportaciones y erosiona el capital de trabajo de las procesadoras. Estas compa\u00f1\u00edas tienen que pagar el 10,5\u0025 de IVA cuando compran la materia prima (soja), y cuando embarcan perciben la devoluci\u00f3n. Pero con argucias insostenibles el gobierno escatima los reintegros, acumulando ya una deuda de 600 millones de pesos.\n\nEl contraste queda subrayado por gruesos trazos de evidencias. El mundo quiere prote\u00ednas animales. La Argentina se especializ\u00f3 en proveer los insumos estrat\u00e9gicos para producirlas. Sin plan alguno, sin medidas de apoyo concretas. Muchas veces, con el lastre de un Estado que, lejos de facilitar el camino, s\u00f3lo ve al sector como una fuente inagotable de recursos fiscales. Se agota.\n \n\nAutor H\u00e9ctor Huergo", + "privacy": { + "value": "" + }, + "type": "status", + "created_time": "2013-07-26T22:45:12+0000", + "updated_time": "2013-07-26T22:45:12+0000" + }, + { + "id": "1057554989_10201127331719566", + "from": { + "name": "Jimoff Foxytrail", + "id": "1057554989" + }, + "message": "From China with love\nRK3188 tha ultimate powah", + "picture": "https://fbcdn-photos-h-a.akamaihd.net/hphotos-ak-prn2/971398_10201127310479035_1515442909_t.jpg", + "link": "https://www.facebook.com/photo.php?fbid=10201127310479035&set=pcb.10201127331719566&type=1&relevant_count=2", + "icon": "https://fbstatic-a.akamaihd.net/rsrc.php/v2/yz/r/StEh3RhPvjk.gif", + "privacy": { + "value": "" + }, + "type": "photo", + "object_id": "10201127310479035", + "created_time": "2013-07-26T22:45:11+0000", + "updated_time": "2013-07-26T22:45:11+0000" + }, + { + "id": "100000795753342_548363101866877", + "from": { + "name": "Natashia Dunn", + "id": "100000795753342" + }, + "message": "\"Did you know in China it remains illegal for anyone to worship outside of the state controlled official churches? And in 2012, the Chinese government spent more money on policing and monitoring its own people than on the entire Chinese military? This alarming development, which began in 2010, is indicative of the ever-rising level of surveillance, restrictions, and control over the Chinese population that keeps true religious freedom for the countries millions of Christians a distant dream.\"\n\nDownload your FREE Special Report on China, and learn how the country is still persecuting Christians: http://ow.ly/nfqWs", + "story": "Natashia Dunn shared International Christian Concern's photo.", + "story_tags": { + "0": [ + { + "id": "100000795753342", + "name": "Natashia Dunn", + "offset": 0, + "length": 13, + "type": "user" + } + ], + "21": [ + { + "id": "19437907453", + "name": "International Christian Concern", + "offset": 21, + "length": 31, + "type": "page" + } + ] + }, + "picture": "https://fbcdn-photos-g-a.akamaihd.net/hphotos-ak-frc1/999080_10151959662667454_487300395_s.jpg", + "link": "https://www.facebook.com/photo.php?fbid=10151959662667454&set=a.154879247453.116636.19437907453&type=1", + "name": "Timeline Photos", + "caption": "ICC SPECIAL REPORT | Is #China Still Persecuting Christians? \n\nDid you know in China it remains illegal for anyone to worship outside of the state controlled official churches? And in 2012, the Chinese government spent more money on policing and monitoring its own people than on the entire Chinese military? This alarming development, which began in 2010, is indicative of the ever-rising level of surveillance, restrictions, and control over the Chinese population that keeps true religious freedom for the countries millions of Christians a distant dream. \n\nDownload your FREE Special Report on China, and learn how the country is still persecuting Christians: http://ow.ly/nfqWs", + "properties": [ + { + "name": "By", + "text": "International Christian Concern", + "href": "https://www.facebook.com/persecuted?ref=stream" + } + ], + "icon": "https://fbstatic-a.akamaihd.net/rsrc.php/v2/yD/r/aS8ecmYRys0.gif", + "privacy": { + "value": "" + }, + "type": "photo", + "object_id": "10151959662667454", + "application": { + "name": "Photos", + "id": "2305272732" + }, + "created_time": "2013-07-26T22:45:07+0000", + "updated_time": "2013-07-26T22:45:07+0000" + }, + { + "id": "100000474770512_696412553717897", + "from": { + "name": "Hiram Guevara Mireles", + "id": "100000474770512" + }, + "message": "Jane y la china...", + "story": "Hiram Guevara Mireles shared a photo.", + "story_tags": { + "0": [ + { + "id": "100000474770512", + "name": "Hiram Guevara Mireles", + "offset": 0, + "length": 21, + "type": "user" + } + ] + }, + "picture": "https://fbcdn-photos-c-a.akamaihd.net/hphotos-ak-ash3/45072_696397510386068_1206903966_s.jpg", + "link": "https://www.facebook.com/photo.php?fbid=696397510386068&set=p.696397510386068&type=1", + "name": "Hiram Guevara Mireles's Photos", + "properties": [ + { + "name": "By", + "text": "Hiram Guevara Mireles", + "href": "https://www.facebook.com/elchee.guevaramireles" + } + ], + "icon": "https://fbstatic-a.akamaihd.net/rsrc.php/v2/yD/r/aS8ecmYRys0.gif", + "privacy": { + "value": "" + }, + "type": "photo", + "object_id": "696397510386068", + "application": { + "name": "Photos", + "id": "2305272732" + }, + "created_time": "2013-07-26T22:45:00+0000", + "updated_time": "2013-07-26T22:45:00+0000" + }, + { + "id": "100000776749057_545215052181048", + "from": { + "name": "Gloria Tadili-Gamo", + "id": "100000776749057" + }, + "message": "CHINA", + "story": "Gloria Tadili-Gamo shared Mysterious Things in The World's photo.", + "story_tags": { + "0": [ + { + "id": "100000776749057", + "name": "Gloria Tadili-Gamo", + "offset": 0, + "length": 18, + "type": "user" + } + ], + "26": [ + { + "id": "257620137692376", + "name": "Mysterious Things in The World", + "offset": 26, + "length": 30, + "type": "page" + } + ] + }, + "picture": "https://photos-a.xx.fbcdn.net/hphotos-prn2/1075732_401373079983747_1793775658_s.jpg", + "link": "https://www.facebook.com/photo.php?fbid=401373079983747&set=a.257929444328112.60568.257620137692376&type=1", + "name": "Timeline Photos", + "caption": "Houses in Sichuan, China.", + "properties": [ + { + "name": "By", + "text": "Mysterious Things in The World", + "href": "https://www.facebook.com/pages/Mysterious-Things-in-The-World/257620137692376?ref=stream" + } + ], + "icon": "https://fbstatic-a.akamaihd.net/rsrc.php/v2/yD/r/aS8ecmYRys0.gif", + "privacy": { + "value": "" + }, + "type": "photo", + "object_id": "401373079983747", + "application": { + "name": "Photos", + "id": "2305272732" + }, + "created_time": "2013-07-26T22:44:58+0000", + "updated_time": "2013-07-26T22:44:58+0000" + }, + { + "id": "100006362920608_1394283104127107", + "from": { + "name": "Tim NetherlandsMogens", + "id": "100006362920608" + }, + "message": "(GOD DAMN IT... Slept with Austria cause im awesome... I WAS ONE DAY AWAY FROM ICELAND OR PRUSSIA!!! *FLIPS EUROPE*)\nCooked with China because he got all Pedo? O.o.... well okaaayyy... ", + "story": "Tim NetherlandsMogens shared Hetalia Yaoi's photo.", + "story_tags": { + "0": [ + { + "id": "100006362920608", + "name": "Tim NetherlandsMogens", + "offset": 0, + "length": 21, + "type": "user" + } + ], + "29": [ + { + "id": "533981726665359", + "name": "Hetalia Yaoi", + "offset": 29, + "length": 12, + "type": "page" + } + ] + }, + "picture": "https://photos-a.xx.fbcdn.net/hphotos-ash4/1045214_556988204364711_219381721_s.jpg", + "link": "https://www.facebook.com/photo.php?fbid=556988204364711&set=a.534003813329817.1073741829.533981726665359&type=1", + "name": "Timeline Photos", + "caption": "( got handcuffed for life with England because he was scared ) - admin niiko", + "properties": [ + { + "name": "By", + "text": "Hetalia Yaoi", + "href": "https://www.facebook.com/pages/Hetalia-Yaoi/533981726665359?ref=stream" + } + ], + "icon": "https://fbstatic-a.akamaihd.net/rsrc.php/v2/yD/r/aS8ecmYRys0.gif", + "privacy": { + "value": "" + }, + "type": "photo", + "object_id": "556988204364711", + "application": { + "name": "Photos", + "id": "2305272732" + }, + "created_time": "2013-07-26T22:44:58+0000", + "updated_time": "2013-07-26T22:44:58+0000" + }, + { + "id": "143419139071479_515030865243636", + "from": { + "category": "Teacher", + "name": "Profesor Gonzalo Rodrigo Mel\u00e9ndez", + "id": "143419139071479" + }, + "message": "Se conmemora el 60 aniversario del Asalto al Cuartel Moncada (1953), primer gran acto del proceso insurreccional del Movimiento 26 de julio que posibilit\u00f3 el triunfo revolucionario en Cuba 6 a\u00f1os m\u00e1s tarde....\n \nSe conmemora el 60 aniversario del Asalto al Cuartel Moncada (1953), primer gran acto del proceso insurreccional del Movimiento 26 de julio que posibilit\u00f3 el triunfo revolucionario en Cuba 6 a\u00f1os m\u00e1s tarde. Lo primero que queremos destacar es el profundo significado de aquella fecha para las revolucionarias y revolucionarios organizados de dentro y fuera de Cuba. Y nada mejor para ello que recordar las maravillosas palabras que el Che escribi\u00f3 en su diario en julio de 1967 en la selva boliviana: \"Por la noche di una peque\u00f1a charla sobre el significado del 26 de julio: rebeli\u00f3n contra las oligarqu\u00edas y contra los dogmas revolucionarios.\u201d Pero m\u00e1s all\u00e1 de las ense\u00f1anzas concretas que se extraen de aquel hecho hist\u00f3rico y del Movimiento que gener\u00f3, en lo que refiere al \u201cdeber de todo revolucionario [de] hacer la revoluci\u00f3n\u201d (Fidel), queremos aprovechar esta conmemoraci\u00f3n para reflexionar sobre el alcance que ha tenido la construcci\u00f3n socialista en Cuba que aquellos acontecimientos posibilitaron. Y ya no s\u00f3lo en clave nacional de aquel querido pa\u00eds, sino en lo que nos muestra e ilumina en el terreno internacional, a\u00fan m\u00e1s si tenemos en cuenta la grav\u00edsima crisis sist\u00e9mica actual de un capitalismo que est\u00e1 llevando al centro mismo del sistema una verdadera guerra social contra los pueblos con especial incidencia en pa\u00edses como el nuestro.\nEfectivamente, vivimos tiempos en que la crisis estructural del sistema capitalista est\u00e1 agudizando progresivamente las contradicciones propias de la lucha de clases. En momentos como estos, en los que todos los derechos sociales y laborales del pueblo trabajador est\u00e1n siendo saqueados por la oligarqu\u00eda que detenta el poder econ\u00f3mico y pol\u00edtico real, es importante que reafirmemos que no existe otro capitalismo posible, pero que otro mundo s\u00ed es posible sin capitalismo. Pues bien, no nos cabe duda de que la Revoluci\u00f3n Cubana ha demostrado ser uno de los pilares m\u00e1s s\u00f3lidos de la lucha mundial del pueblo trabajador por conquistar su libertad. Los logros obtenidos por el pueblo cubano son m\u00e1s relevantes a\u00fan si tenemos en cuenta que se han conseguido perteneciendo al campo hist\u00f3rico de pa\u00edses expoliados del llamado Tercer Mundo. Ese bienestar de Cuba a\u00fan se realza m\u00e1s relativamente cuando reparamos en que no es resultado de ninguno de esos \u201cestados del bienestar\u201d que se han dado en el campo de pa\u00edses desarrollados e imperialistas y que, en buena parte, son producto criminal de la explotaci\u00f3n y opresi\u00f3n \u2013remota y presente, colonial y neocolonial- contra pa\u00edses precisamente como Cuba. Una explotaci\u00f3n que es de largo alcance en el pasado y que no puede dejar de marcar por mucho tiempo a\u00fan tras los procesos de liberaci\u00f3n que se dan en ese tipo de pa\u00edses.\nAdem\u00e1s, en el caso particular de Cuba, todav\u00eda se realzan m\u00e1s sus conquistas por el marco geoestrat\u00e9gico en que se encuentra: est\u00e1 situada a s\u00f3lo 90 millas de Estados Unidos, el pa\u00eds imperialista con m\u00e1s potencia armament\u00edstica y el que m\u00e1s intervenciones militares contra otros pa\u00edses ha efectuado en la historia, especialmente en Am\u00e9rica Latina.\nLa Rep\u00fablica de Cuba siempre ha estado marcada por la relaci\u00f3n mantenida con EEUU, incluso desde antes del triunfo de la Revoluci\u00f3n. Desde la \u201cindependencia\u201d de Cuba en 1901 hasta 1933 exist\u00eda en la Constituci\u00f3n cubana una cl\u00e1usula que permit\u00eda legalmente la intervenci\u00f3n militar estadounidense en determinadas circunstancias1. A principios de los a\u00f1os 50, las empresas norteamericanas controlaban la mayor parte de la producci\u00f3n econ\u00f3mica de la isla, como por ejemplo el 90\u0025 de la electricidad y telefon\u00eda, el 100\u0025 de la producci\u00f3n de n\u00edquel o casi el 50\u0025 de la industria azucarera. Con esos datos en la mano, puede verse c\u00f3mo, pese a que Cuba era nominalmente independiente, en la pr\u00e1ctica estaba bajo el dominio del imperialismo norteamericano.\nBajo la dictadura militar de Fulgencio Batista, lacayo de EEUU, se agrava el cuadro de desolaci\u00f3n y de crisis \u00e9tica y pol\u00edtico-social del pa\u00eds. El golpe militar hab\u00eda agudizado todas las contradicciones de la isla y hab\u00eda puesto de manifiesto el vac\u00edo de direcci\u00f3n nacional frente a la dictadura y la incapacidad de los partidos pol\u00edticos, enredados en in\u00fatiles divergencias y sin visi\u00f3n hist\u00f3rica de aquel momento crucial. Fue en medio de esa oscuridad cuando, precisamente el 26 de julio de 1953 que hoy conmemoramos, un centenar de j\u00f3venes llevaron a cabo el asalto al cuartel de Moncada, con el que intentaban iniciar el derrocamiento del dictador. Aunque esa acci\u00f3n fracas\u00f3 militarmente, la heroicidad de los combatientes repercuti\u00f3 de manera decisiva en la situaci\u00f3n pol\u00edtica y social de toda la Isla. Algunos de los participantes exiliados volvieron a Cuba en 1956. Hab\u00eda sonado el comienzo de la Revoluci\u00f3n Cubana. El foco guerrillero inicial fue convirti\u00e9ndose con el tiempo en todo un ej\u00e9rcito rebelde, que con el apoyo del pueblo trabajador derroc\u00f3 a las fuerzas de Batista en 1959.\nDesde ese momento, comenz\u00f3 un proceso de transformaci\u00f3n social que dura hasta hoy a pesar de todos los intentos del imperialismo (desde desestabilizaciones sutiles hasta invasiones directas) por impedir que la riqueza de la isla y el poder est\u00e9n en manos del pueblo.\nAunque los intentos de asesinato de Fidel Castro por parte de EEUU se cuentan por centenares2, la estrategia general ha sido asfixiar a la isla mediante un bloqueo econ\u00f3mico que dura ya m\u00e1s de cincuenta a\u00f1os. Durante mucho tiempo, el \u00fanico soporte econ\u00f3mico para la Rep\u00fablica de Cuba fue el respaldo de la Uni\u00f3n Sovi\u00e9tica, por lo que tras el colapso del bloque del Este la isla se vio inmersa en la peor crisis econ\u00f3mica de su historia, con una ca\u00edda del PIB del 30\u0025, EEUU recrudeci\u00f3 el embargo, creyendo que era el fin del proceso socialista en Cuba. Sin embargo, la voluntad firme del pueblo y su apoyo incondicional a la Revoluci\u00f3n consiguieron que Cuba saliese a flote.\nEs importante se\u00f1alar, ahora que todos nuestros derechos est\u00e1n siendo aniquilados con el pretexto de que es \u201cinevitable\u201d recortar a causa de la crisis, que en Cuba el gasto en Sanidad o Educaci\u00f3n no disminuy\u00f3 un \u00e1pice en esos a\u00f1os duros3, por lo que no se produjo el salvaje aumento de mortalidad que s\u00ed se ha producido en otros pa\u00edses que han privatizado los servicios p\u00fablicos como respuesta a una crisis, como Grecia, y que es esperable que se produzca en el Estado espa\u00f1ol.\nMientras vemos c\u00f3mo la edad de jubilaci\u00f3n se prolonga hasta los 67 a\u00f1os, c\u00f3mo los recortes y las privatizaciones causan que haya cada vez m\u00e1s familias trabajadoras desahuciadas, gente con enfermedades cr\u00f3nicas sin posibilidad de acceder a los medicamentos que necesitan, o cada vez menos personas puedan acceder a los estudios universitarios por el aumento de las tasas, los logros conseguidos por la revoluci\u00f3n cubana brillan por s\u00ed mismos.\nSeg\u00fan la ONU, UNICEF, ONEC, WWF o UNESCO, entidades que consideramos poco sospechosas parcialidad pro-cubana:\n\u2022 Cuba es el \u00fanico pa\u00eds de Am\u00e9rica Latina sin desnutrici\u00f3n infantil o problemas de drogas.\n\u2022 No existen personas analfabetas en Cuba. El \u00edndice de escolarizaci\u00f3n primaria es del 100\u0025. El de educaci\u00f3n secundaria, un 99.7\u0025 (mientras que el promedio en otros pa\u00edses latinoamericanos ronda el 50\u0025) y la ense\u00f1anza es totalmente gratuita, incluido los libros, desde la escuela infantil a la universidad.\n\u2022 Es el \u00fanico pa\u00eds del mundo que cumple los criterios de sostenibilidad ecol\u00f3gica. -Es el pa\u00eds que m\u00e1s n\u00famero de m\u00e9dicos tiene por habitante en todo el mundo, aproximadamente el doble que Reino Unido.\n\u2022 En Am\u00e9rica Latina, un 20\u0025 de la poblaci\u00f3n (aproximadamente 102 millones) subsiste en la completa indigencia. Ninguna de esas personas vive en Cuba.\n\u2022 El \u00cdndice de Desarrollo Humano4 en Cuba es de 0.8 sobre 1.\n\u2022 Posee la tasa de mortalidad m\u00e1s baja de Am\u00e9rica Latina.\n\u2022 Su tasa de mortalidad infantil es inferior a la de muchas zonas de EEUU.\nSi, tal como ya hemos hecho menci\u00f3n, tenemos en cuenta los l\u00edmites hist\u00f3ricos y las agresiones sufridas por Cuba, y que de aqu\u00ed partir\u00edamos de un nivel superior de desarrollo material, los ejemplos citados demuestran que es posible crear una sociedad donde la econom\u00eda sea planificada para cubrir las necesidades reales de la poblaci\u00f3n, y no para el enriquecimiento de una minor\u00eda.\nPero no s\u00f3lo la Rep\u00fablica de Cuba garantiza los derechos sociales necesarios para su poblaci\u00f3n. En medio de provocaciones y de agresiones continuas, que no pueden dejar de limitar el ejercicio realmente democr\u00e1tico a que toda aut\u00e9ntica revoluci\u00f3n aspira, en Cuba hay elecciones no partidistas, en las que cualquier persona puede presentarse cada 5 o cada 2 a\u00f1os (dependiendo de si son generales o locales). Sin embargo, es tachada de dictadura por EEUU, el \u00fanico pa\u00eds que ha usado armas nucleares contra poblaci\u00f3n civil y el que m\u00e1s naciones ha invadido y expoliado en toda la historia de la humanidad.\nAunque estos datos por s\u00ed solos hacen que cualquier persona en sus cabales defienda el sistemacubano, los logros del socialismo en la isla van m\u00e1s all\u00e1 de unas meras cifras, por muy importantes que sean.\nPor ejemplo, en el campo de la izquierda revolucionaria y las fuerzas anti-imperialistas, Cuba es un modelo a seguir por su apoyo firme a todos los procesos enmancipadores y su rechazo incondicional a toda dominaci\u00f3n imperialista. Este compromiso de Cuba se expres\u00f3, en el plano militar, como ayuda directa a los pa\u00edses que luchaban por su liberaci\u00f3n.\nLa Cuba revolucionaria fue, y sigue siendo en gran medida, un ejemplo de internacionalismo real, al apoyar con todos sus medios la creaci\u00f3n de focos guerrilleros en Nicaragua, Colombia, Bolivia, el Congo y muchos otros lugares; y al contribuir igualmente en la resistencia antiimperialista de pa\u00edses como Argelia, Angola o Vietnam. Cabe destacar la decisiva participaci\u00f3n cubana, junto a las tropas angole\u00f1as en la derrota de la Sud\u00e1frica del Apartheid en Angola, donde menci\u00f3n especial merece la batalla de Cuito Cuanavale.\nPero, adem\u00e1s, hay que destacar que la Revoluci\u00f3n Cubana, desde sus inicios hasta ahora mismo, ha enviado ayuda sanitaria y formado m\u00e9dicos para pa\u00edses de \u00c1frica, Asia y Am\u00e9rica Latina, incluso en circunstancias extremadamente dif\u00edciles. Ya m\u00e1s actualmente, sin Cuba no puede entenderse la emergencia de la Revoluci\u00f3n Bolivariana en Venezuela, germinando una semilla de libertad que sigue rebrotando en toda Latinoam\u00e9rica.\nPor otro lado, hay que valorar los logros cubanos en materia de g\u00e9nero. Desde Red Roja afirmamos que el socialismo no trae autom\u00e1ticamente la liberaci\u00f3n de las mujeres (aunque sea un requisito para la misma). Si bien sabemos, como no pod\u00eda ser de otra manera, que la lucha antipatriarcal en Cuba est\u00e1 lejos de terminar, podemos sostener que se han producido importantes avances feministas gracias al socialismo cubano: un indicador entre otros es que, actualmente, m\u00e1s de la mitad de las personas electas como representantes del pueblo en la Asamblea General del Poder Popular, el parlamentocubano, son mujeres5. Adem\u00e1s todos los m\u00e9todos de planificaci\u00f3n familiar, incluido el aborto, son libres y gratuitos para toda la poblaci\u00f3n.\nPara Red Roja, la defensa de la Revoluci\u00f3n y el socialismo en Cuba es una cuesti\u00f3n de principios, por m\u00e1s que no seamos acr\u00edticos con el devenir de la Rep\u00fablica de Cuba, dado que eso ser\u00eda hacer un flaco favor tanto al pueblo cubano como a nosotras y nosotros mismos. Muy al contrario, nuestra postura nos permite aprender tanto de sus victorias como de sus errores; aunque siempre mantendremos esa posici\u00f3n desde el apoyo firme desde un lineamiento antiimperialista, el nuestro, que no pone el acento en el agredido sino en el agresor.\nEn definitiva, adem\u00e1s de la importancia indudable que tiene en su marco estatal concreto, Cuba, al igual que cualquier otra experiencia socialista o revolucionaria como lo fueron la URSS o la China Popular, o como lo son los destacamentos guerrilleros de Colombia o la India, es algo m\u00e1s. Su mayor importancia es la aportaci\u00f3n que realiza al campo de la lucha revolucionaria anticapitalista a escala mundial, y las ense\u00f1anzas que podemos extraer de estas experiencias concretas a la hora de poner en marcha la construcci\u00f3n del socialismo y el desarrollo del comunismo en todos los lugares del mundo. De ah\u00ed que, 60 a\u00f1os despu\u00e9s de aquel glorioso 26 de Julio de 1953, queramos destacar la dimensi\u00f3n internacional y la proyecci\u00f3n de largo alcance hist\u00f3rico de aquella fecha. Y que podamos gritar:\nSiempre es 26 de Julio.\nComo siempre es 8 de Marzo.\nComo siempre es 1 de Mayo.\nViva la revoluci\u00f3n cubana.\nViva la lucha de la clase obrera.", + "actions": [ + { + "name": "Comment", + "link": "https://www.facebook.com/143419139071479/posts/515030865243636" + } + ], + "privacy": { + "value": "" + }, + "type": "status", + "created_time": "2013-07-26T22:44:57+0000", + "updated_time": "2013-07-26T22:44:57+0000" + }, + { + "id": "596665924_10151496285490925", + "from": { + "name": "Julio Cesar Mendivil Moroyoqui", + "id": "596665924" + }, + "message": "COMIDA CHINA PLAZA SENDERO!!!!!!", + "story": "Julio Cesar Mendivil Moroyoqui shared Karlita Vargas's photo.", + "story_tags": { + "0": [ + { + "id": "596665924", + "name": "Julio Cesar Mendivil Moroyoqui", + "offset": 0, + "length": 30, + "type": "user" + } + ], + "38": [ + { + "id": "100002045694903", + "name": "Karlita Vargas", + "offset": 38, + "length": 14, + "type": "user" + } + ] + }, + "picture": "https://photos-a.xx.fbcdn.net/hphotos-prn1/59203_491218377623014_476883201_s.jpg", + "link": "https://www.facebook.com/photo.php?fbid=491218377623014&set=p.491218377623014&type=1", + "name": "Karlita Vargas's Photos", + "caption": "Compartan, es lo que sirven de comer en la COMIDA CHINA en PLAZA SENDERO... Compartan por favor", + "properties": [ + { + "name": "By", + "text": "Karlita Vargas", + "href": "https://www.facebook.com/karlita.vargas.397" + } + ], + "icon": "https://fbstatic-a.akamaihd.net/rsrc.php/v2/yD/r/aS8ecmYRys0.gif", + "privacy": { + "value": "" + }, + "type": "photo", + "object_id": "491218377623014", + "application": { + "name": "Links", + "id": "2309869772" + }, + "created_time": "2013-07-26T22:44:50+0000", + "updated_time": "2013-07-26T22:44:50+0000" + } + ], + "paging": { + "previous": "https://graph.facebook.com/search?q=china&type=post&access_token=CAADysJa3mtcBADbzkQdT8LjgTNAJEZAZC36Ol7PwArlMIwLlDeDv1bn415WRGYIg3TCOlGMDpmtA7tWhFkSHZBuqtkUivghE7ZBtNSDabwFUt9hRZCeqA3uh5ttXBahQJAbCjrONcmfu9Kf4KQKUl&limit=25&since=1374878754&__previous=1", + "next": "https://graph.facebook.com/search?q=china&type=post&access_token=CAADysJa3mtcBADbzkQdT8LjgTNAJEZAZC36Ol7PwArlMIwLlDeDv1bn415WRGYIg3TCOlGMDpmtA7tWhFkSHZBuqtkUivghE7ZBtNSDabwFUt9hRZCeqA3uh5ttXBahQJAbCjrONcmfu9Kf4KQKUl&limit=25&until=1374878689" + } +} \ No newline at end of file diff --git a/tests/src/test/resources/facebook2.json b/tests/src/test/resources/facebook2.json new file mode 100644 index 00000000..b1b0ae54 --- /dev/null +++ b/tests/src/test/resources/facebook2.json @@ -0,0 +1,555 @@ +{ + "data": [ + { + "id": "100004872312380_180849772087432", + "from": { + "name": "Eleyn Emerald Lopez", + "id": "100004872312380" + }, + "message": "bago na pala ang fb hindi ako nainform lol", + "privacy": { + "value": "" + }, + "type": "status", + "created_time": "2013-07-26T22:57:09+0000", + "updated_time": "2013-07-26T22:57:09+0000" + }, + { + "id": "100003186250897_430526510396887", + "from": { + "name": "Elisa Cabusbusan", + "id": "100003186250897" + }, + "message": "Marami sa atin na gusto magkaroon ng additional income...tama??\r\nDahil sa panahon ngayon,,hindi cla kuntento sa tinatawag nting single income...\r\nAba gusto mo bang malaman kung paano??\r\nPls.panoorin nyo ito at tiyak malaking tulong sa buhay ntin at sa mga frnds ntin na gusto rin kumita...", + "picture": "https://fbexternal-a.akamaihd.net/safe_image.php?d=AQCMWRgVZjKdzdzh&w=130&h=130&url=http\u00253A\u00252F\u00252Fi1.ytimg.com\u00252Fvi\u00252FWWAlCiFTa_0\u00252Fmaxresdefault.jpg\u00253Ffeature\u00253Dog", + "link": "http://www.youtube.com/watch?v=WWAlCiFTa_0&sns=fb", + "source": "http://www.youtube.com/v/WWAlCiFTa_0?autohide=1&version=3&autoplay=1", + "name": "Elisa Cabusbusan", + "icon": "https://fbstatic-a.akamaihd.net/rsrc.php/v2/yj/r/v2OnaTyTQZE.gif", + "privacy": { + "value": "" + }, + "type": "video", + "application": { + "name": "Share_bookmarklet", + "id": "5085647995" + }, + "created_time": "2013-07-26T22:57:08+0000", + "updated_time": "2013-07-26T22:57:08+0000" + }, + { + "id": "283855334990816_582660281776985", + "from": { + "category": "Community", + "name": "Tagpuan ng Mga Inlove at Brokenhearted", + "id": "283855334990816" + }, + "message": "Hindi mo naman kailangan ang sobrang bless sing yan tibok ng puso mo ay sapat na..kaya ugaliin paggising sa umaga manalangin magpasalamat sa panginoon kung ang puso mo ay tumitibok pa para harapin ang bagong pag-asa.\n\nGandang umaga!!!\nHoney!!!", + "actions": [ + { + "name": "Comment", + "link": "https://www.facebook.com/283855334990816/posts/582660281776985" + } + ], + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Facebook for Android", + "namespace": "fbandroid", + "id": "350685531728" + }, + "created_time": "2013-07-26T22:57:05+0000", + "updated_time": "2013-07-26T22:57:05+0000" + }, + { + "id": "100002130420335_502681236479553", + "from": { + "name": "Sally Tavares", + "id": "100002130420335" + }, + "message": "Minsan ang taong nagbibigay sayo ng iNis,\nAy ang taong mahaL mo kaya hindi mo matiis !", + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Facebook for Android", + "namespace": "fbandroid", + "id": "350685531728" + }, + "created_time": "2013-07-26T22:57:00+0000", + "updated_time": "2013-07-26T22:57:00+0000" + }, + { + "id": "100002431768892_490719781019076", + "from": { + "name": "Lorlyn Umas-as", + "id": "100002431768892" + }, + "message": "Kung Mahal Mo Sya Hinding Hindi Ka Titingin Sa Iba Pwera Na Lang Kung malandi Ka.", + "privacy": { + "value": "" + }, + "type": "status", + "created_time": "2013-07-26T22:56:58+0000", + "updated_time": "2013-07-26T22:56:58+0000" + }, + { + "id": "100006081401204_1403689419843758", + "from": { + "name": "Ai Shi Teru J", + "id": "100006081401204" + }, + "message": "Cass!! hindi ko na i p pm sayo to para mabasa rin nya!! matanda ka sakin Cass dapat act like a grown up woman... ikaw naman kuya JC did you ever forget that hurting girls can be like hurting your mother? 6 na suntok sa muka? namamagang muka at mga labi napapaiayak na lang ako sa pinsan ko eh!! ang akala ko ALIEN ka kasi iba ka sa mga lalake pero hindi masasabi ko lang na 1\u0025 lang ng lalake ang hindi nananakit!! pasalamat ka at nasa states mommy ko at si tita, at wala ang dad nya nasa bussines trip kung hindi kulong abot mo! pwede ka ng makulong kaai 19 yrs old ka na, so please kuya JC ayusin nyo na pu yan nakakaawa si ate cassandra, May the lord heal all the wounds from ate cass's heart...., TY na rin po kuya sana magtagal pa kayo but don't hurt ate Cass i love you both!!", + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Facebook for iPhone", + "namespace": "fbiphone", + "id": "6628568379" + }, + "created_time": "2013-07-26T22:56:54+0000", + "updated_time": "2013-07-26T22:56:54+0000" + }, + { + "id": "100000312507455_605031206183957", + "from": { + "name": "Noel Taguimacon Aligoy", + "id": "100000312507455" + }, + "message": "Kung may pagsubok na dumarating sa buhay mo...magpasalamat ka sa Diyos at humingi ng gabay sa Kanya.. kasi hindi ka bibigyan ng pagsubok na hindi mo kaya at hindi ka Niya iiwanan basta't tawagin mo lang Siya", + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Mobile", + "id": "2915120374" + }, + "created_time": "2013-07-26T22:56:47+0000", + "updated_time": "2013-07-26T22:56:47+0000" + }, + { + "id": "100000701715522_623161457717239", + "from": { + "name": "Ittaqillah Ya Ikhwan", + "id": "100000701715522" + }, + "message": "check list ya ikhwaan sa pag nag aayuno sa mundo ng fb:\n\u221a no chatting sa pm, lalo sa hindi mahram,\n\u221a no browsing sa mga pages na nagkakasala ka.\n\u221a no posting ng mga bagay na nagkakasala ka.\n\u221a no tsikahan o panlilibak sa wall man o pm\n\u2193\u2193\u2193\u2193\u2193\u2193\u2193\u2193\n\npero teka muna, pagkatapos ng iftar?\n\nbakit lahat ng ito ay nagiging HALAL na rin?\n\n[paalala lamang sa aking sarili at sa mga kapatid kong mahal]", + "privacy": { + "value": "" + }, + "type": "status", + "created_time": "2013-07-26T22:56:45+0000", + "updated_time": "2013-07-26T22:56:45+0000" + }, + { + "id": "100000091490096_633636329982744", + "from": { + "name": "Jorjohn Bautista", + "id": "100000091490096" + }, + "message": "Umuwi ako at naisipan ko siyang tawagan, naiiisp ko na ang kanyang isasagot... hindi ako nagkamali... salamat sa iyong pakikisama sa akin kahit na alam kong puro sablay, ngunit ngayong nakausap na kita ako ay naliwanagan... salamat sa iyo, madami akong napagtanto sa aking sarili marami pala akong paniniwala na akala ko ay tama... salamat dahil ako ay iyong minahal, hanggang duon na lang aking mahal ako ay hahayo sa ibang bersikulo ng aking buhay baon ang iyong mga pangaral sa masalimuot kong buhay... salamat!!! \n\n=D", + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Facebook for Android", + "namespace": "fbandroid", + "id": "350685531728" + }, + "created_time": "2013-07-26T22:56:44+0000", + "updated_time": "2013-07-26T22:56:44+0000" + }, + { + "id": "100003852196693_277640522374352", + "from": { + "name": "Lienzion Lamis Gomid", + "id": "100003852196693" + }, + "message": "Ang ex q hindi nagrply sa txt q..natakot n pagalitan q..anu kaya ang ginawa nya sa cp q...ex q hindi m talaga inigatan ang cp q ha...alam muna n love q ang cp q....aammm...\n\nJay egam buhea\"", + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Facebook for Android", + "namespace": "fbandroid", + "id": "350685531728" + }, + "created_time": "2013-07-26T22:56:36+0000", + "updated_time": "2013-07-26T22:56:36+0000" + }, + { + "id": "100001367121966_525989724123260", + "from": { + "name": "Kemin Guialal", + "id": "100001367121966" + }, + "message": "Mahulog ka na ng paulit-ulit sa KANAL.\n\nWag lang sa tao na HINDI KA MAHAL!\n\n#BOOM !\n\n\u30c4kEmin_Lamang\u10e6", + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Mobile", + "id": "2915120374" + }, + "created_time": "2013-07-26T22:56:32+0000", + "updated_time": "2013-07-26T22:56:32+0000" + }, + { + "id": "100004631591701_205164966314555", + "from": { + "name": "Mylene Loyola", + "id": "100004631591701" + }, + "message": "Manalangin tayo\n\nPanginoon,Thank you for all the blessing for all the guidance, inilalayo nyo po kami sa kapahamakan,maraming maraming salamat po Panginoon. Alam po namin Panginoon kami'y makasalanan na kahit kung minsan nakakagawa kami ng hindi maganda sa paningin ninyo andiyan pa rin kayo parating nakagabay at handang tumulong sa oras ng aming pangangailangan, I'm so sorry Lord that I have been going on our own way instead of your way, thank you Lord for dying to take away my sins,sa walang sawang pag patnubay sa aming buong pamilya kami rin ay taos pusong nagpapasalamat sayo Panginoon,Tinataas po namin Panginoon ang patuloy na proteksyon sa aming buong pamilya at mga mahal sa buhay. Alam po namin na kayo lang ang nakaka alam ng mas nakakabuti sa amin kaya sayo lang namin ipinapaubaya ang lahat sa walang sawang pag patnubay sa aming lahat wala na kaming ibang sasabihin kundi Salamat,salamat at salamat aming PANGINOON....Amen\n\n*dachi* \u2014 with Raine Claudine Salac and 36 others.\nManalangin tayo Panginoon,Thank you for all the blessing for all the guidance, inilalayo nyo po kami sa kapahamakan,maraming maraming salamat po Panginoon. Alam po namin Panginoon kami'y makasalanan na kahit kung minsan nakakagawa kami ng hindi maganda sa paningin ninyo andiyan pa rin kayo parating nakagabay at handang tumulong sa oras ng aming pangangailangan, I'm so sorry Lord that I have been going on our own way instead of your way, thank you Lord for dying to take away my sins,sa walang sawang pag patnubay sa aming buong pamilya kami rin ay taos pusong nagpapasalamat sayo Panginoon,Tinataas po namin Panginoon ang patuloy na proteksyon sa aming buong pamilya at mga mahal sa buhay. Alam po namin na kayo lang ang nakaka alam ng mas nakakabuti sa amin kaya sayo lang namin ipinapaubaya ang lahat sa walang sawang pag patnubay sa aming lahat wala na kaming ibang sasabihin kundi Salamat,salamat at salamat aming PANGINOON....Amen *dachi*", + "privacy": { + "value": "" + }, + "type": "status", + "created_time": "2013-07-26T22:56:27+0000", + "updated_time": "2013-07-26T22:56:27+0000" + }, + { + "id": "399128610155939_545572068844925", + "from": { + "category": "Bank/financial institution", + "name": "Direct loans service repayment", + "id": "399128610155939" + }, + "message": "impotence homeopathy http://impotence.homeopathy.medoonll4.appspot.com/#impotence-homeopathy\nviagra available in uk http://viagra.available.in.uk.medoonll4.appspot.com/#viagra-available-in-uk\nwhat is pink viagra http://what.is.pink.viagra.medoonll4.appspot.com/#what-is-pink-viagra\nviagra men dont need http://viagra.men.dont.need.medoonll4.appspot.com/#viagra-men-dont-need\ncialis quitting http://cialis.quitting.medoonll4.appspot.com/#cialis-quitting\nviagra pain meds http://viagra.pain.meds.medoonll4.appspot.com/#viagra-pain-meds\ntadalafil headaches http://tadalafil.headaches.medoonll4.appspot.com/#tadalafil-headaches\ncanadian viagra email virus http://canadian.viagra.email.virus.medoonll4.appspot.com/#canadian-viagra-email-virus\nactive ingredients in viagra http://active.ingredients.in.viagra.medoonll4.appspot.com/#active-ingredients-in-viagra\nviagra online new zealand http://viagra.online.new.zealand.medoonll4.appspot.com/#viagra-online-new-zealand\nviagra falls film http://viagra.falls.film.medoonll4.appspot.com/#viagra-falls-film\ngenericcialais http://genericcialais.medoonll4.appspot.com/#genericcialais\nclarithromycin cialis http://clarithromycin.cialis.medoonll4.appspot.com/#clarithromycin-cialis\nviagra for men in hindi http://viagra.for.men.in.hindi.medoonll4.appspot.com/#viagra-for-men-in-hindi\nviagra online weekend http://viagra.online.weekend.medoonll4.appspot.com/#viagra-online-weekend\nviagra for sal http://viagra.for.sal.medoonll4.appspot.com/#viagra-for-sal\ncialis use in pregnancy http://cialis.use.in.pregnancy.medoonll4.appspot.com/#cialis-use-in-pregnancy\nlow price generic viagra http://low.price.generic.viagra.medoonll4.appspot.com/#low-price-generic-viagra\nviagra canada buy online http://viagra.canada.buy.online.medoonll4.appspot.com/#viagra-canada-buy-online\ncialis samples of http://cialis.samples.of.medoonll4.appspot.com/#cialis-samples-of\ncialis cigarette http://cialis.cigarette.medoonll4.appspot.com/#cialis-cigarette\nviagra png http://viagra.png.medoonll4.appspot.com/#viagra-png\nviagra pumpkin pie http://viagra.pumpkin.pie.medoonll4.appspot.com/#viagra-pumpkin-pie\npurchase viagra for women http://purchase.viagra.for.women.medoonll4.appspot.com/#purchase-viagra-for-women\nviagra small business http://viagra.small.business.medoonll4.appspot.com/#viagra-small-business", + "actions": [ + { + "name": "Comment", + "link": "https://www.facebook.com/399128610155939/posts/545572068844925" + } + ], + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "PostCron", + "namespace": "postcron", + "id": "178106548898871" + }, + "created_time": "2013-07-26T22:56:26+0000", + "updated_time": "2013-07-26T22:56:26+0000" + }, + { + "id": "100005927225164_142683769272530", + "from": { + "name": "Godwin Gliane", + "id": "100005927225164" + }, + "to": { + "data": [ + { + "name": "Jett Romerson Pagayon", + "id": "100001728618316" + }, + { + "name": "Noel Godwin Gliane Alaurin", + "id": "100003306814234" + } + ] + }, + "message": "Jett Romerson Pagayon.. sabi ko po sayo..\nilang beses na.. hindi kita marereplayan\ndito sa account nato kasi blocked nga to..\n\nadd moto para mareplayan kita \u2192 Noel Godwin Gliane Alaurin", + "message_tags": { + "0": [ + { + "id": "100001728618316", + "name": "Jett Romerson Pagayon", + "type": "user", + "offset": 0, + "length": 21 + } + ], + "159": [ + { + "id": "100003306814234", + "name": "Noel Godwin Gliane Alaurin", + "type": "user", + "offset": 159, + "length": 26 + } + ] + }, + "privacy": { + "value": "" + }, + "type": "status", + "created_time": "2013-07-26T22:56:16+0000", + "updated_time": "2013-07-26T22:56:16+0000" + }, + { + "id": "100003192774512_417021401747615", + "from": { + "name": "Criszel Isiderio", + "id": "100003192774512" + }, + "message": "huh!3days nalng b-day n ng aking pinakakamahal n inay ko.....anu kaya ma ihanda?i want to celebrate..but ayw nya,sayang daw..inay talga minsan nga lng......he...hindi man xa ang gagastus eh....ako man...", + "privacy": { + "value": "" + }, + "type": "status", + "created_time": "2013-07-26T22:56:15+0000", + "updated_time": "2013-07-26T22:56:15+0000" + }, + { + "id": "184003048420213_223788021108382", + "from": { + "category": "Just for fun", + "name": "Wag kang PAASA , please? ang sakit eh", + "id": "184003048420213" + }, + "message": "\"Being UNIQUE is SPECIAL, so be PROUD of YOURSELF. :-D\"\n\n-Good MORNING ! :-D\n-Hindi pa nga nag ALMUSAL facebook agad ..\n-hit like sa mga online ..\n-comment your cp number ...\n-PLUG kita .. :-D\n-first 5 only ... \n\n#adminyou\u003C3", + "actions": [ + { + "name": "Comment", + "link": "https://www.facebook.com/184003048420213/posts/223788021108382" + } + ], + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Mobile", + "id": "2915120374" + }, + "created_time": "2013-07-26T22:56:14+0000", + "updated_time": "2013-07-26T22:56:14+0000", + "likes": { + "data": [ + { + "id": "100003043439049", + "name": "Jericka Larize Gonzales" + } + ], + "paging": { + "cursors": { + "after": "MTAwMDAzMDQzNDM5MDQ5", + "before": "MTAwMDAzMDQzNDM5MDQ5" + } + } + } + }, + { + "id": "256621417799622_368172799977816", + "from": { + "category": "Community", + "name": "Davonaire-Inspirational\"\"", + "id": "256621417799622" + }, + "message": "Ang pagsisinungaling ay isang talento. Talento na sana hindi na lang naimbento\n\n\u2665Cat's Eye\u2665", + "actions": [ + { + "name": "Comment", + "link": "https://www.facebook.com/256621417799622/posts/368172799977816" + } + ], + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Pages Manager for Android", + "namespace": "fbpagemgr_android", + "id": "121876164619130" + }, + "created_time": "2013-07-26T22:56:13+0000", + "updated_time": "2013-07-26T22:56:13+0000" + }, + { + "id": "100000282765695_643110372375049", + "from": { + "name": "Rochel Uniatic", + "id": "100000282765695" + }, + "message": "\u201cFall in love when you\u2019re ready, hindi lang dahil gusto mong gumanti.\u201d", + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Facebook for iPad", + "namespace": "fbipad_", + "id": "173847642670370" + }, + "created_time": "2013-07-26T22:56:07+0000", + "updated_time": "2013-07-26T22:56:07+0000" + }, + { + "id": "100001859593999_517392921666023", + "from": { + "name": "Eloisa Balingit", + "id": "100001859593999" + }, + "message": "Sa lahat ng nakakabasa nito, swerte tayo. Know why? Kapag umalis ka ng bahay at napatambay ka sa lugar tulad ng sakayan ng Jeep. May makikita kang mga barker ng jeep na patuloy ang pagsigaw para maambunan ng limang pisong barya. Tumingin ka sa kanan eh may Ale na nagtitinda ng Palamig at sa harapan mo naman eh may lalakeng nagtitinda ng Pinya. Then uuwi ka ng bahay niyo may makakasalubong kang nagtitinda ng Yakult at hila ang karitela na puno ng Yakult. At paglagpas eh makakakita ka ng matandang Lalake na may dalang malaking FOAM or Aparador para ibenta sa inyo sa tapat ng initan.\n\nKung iisipin mo, ang laki ng ginhawa ng buhay mo ikumpara sa kanila, pero they continue living right?\n\nMeaning.. Kung ano man yung problemang nararanasan natin eh kailangan nating tanggapin at hindi iwasan. Na kailangan nating isipin na yung mga bagay na hindi natin pinoproblema eh pinoproblema ng iba. Hindi ba't nakakalungkot isipin na may mga taong iniwan lang ng JOWA nila eh gusto na nilang mamatay? Without knowing na swerte pa rin siya dahil maginhawa ang buhay niya? Na hindi dapat masyadong pinoproblema ang ganung klase ng problema? Na dapat sa likod ng nangyari sa kanya eh swerte pa rin siya sa buhay hindi tulad ng iba.\n\nMinsan ba sumagi sa isip mo na nababasa mo to kasi swerte ka? Kasi may sarili kang Computer o kaya naman ay may pangbayad ka para makagamit ng Computer. Habang yung iba eh nasa labas ng bahay hindi magkanda ugaga buhayin ang pamilya o kaya mag hanapbuhay. And still sasabihin mong \"Fuck I Hate this Life?\"\n\nSabi nga eh never take things for granted. Hindi lahat ng bagay na meron ka eh nararanasan din ng iba. Magpasalamat tayo na yung mga bagay na naabot ng buhay natin eh inaabot palang ng iba?\n\nMinsan, kailangan mong mapansin yung mga malilit na bagay para malaman mong malaking bagay na pala ang naibigay sayo.\n^________^\n\n:) goodmorning\ngodbless \u003C3", + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Mobile", + "id": "2915120374" + }, + "created_time": "2013-07-26T22:56:04+0000", + "updated_time": "2013-07-26T22:56:04+0000" + }, + { + "id": "100003240641743_405753266209358", + "from": { + "name": "X-nhor Utto", + "id": "100003240641743" + }, + "message": "gnyan ang ugali ni sahod CAPITAL- Yan! Heto na tayo ito ang iniiwasan ng karamihan yung capital. Lagi natin tatandaan sa lahat ng bagay sa mundo kahit hindi negosyo lahat may tinatawag na capital o investments. Walang libre!\n\nSaan ka naman nakakita ng libreng negosyo? Aber nga? \n\nKapag ba naghahanap ka ng trabaho sa palagay mo wala kang invested capital? O diba gumagawa ka ng paraan para makagawa ng Resume or Biodata para maipadala mo sa mga companies na gusto mong pasukan hoping na matanggap ka. Tapos magpapagawa ka ng ID pictures na cute na cute ka para maattract ang Employer sayo? Ngayon kung ipatawag ka for interviews diba bibili ka ng magandang damit para presentable ka naman e ang pamasahe pa? ...ibig sabihin nag-invest ka ng time & money para sa minimum wages na kikitain mo. Para payamanin mo ang amo mo!\n\nSa AIM Global, once ka lang maglalabas ng capital sabayan mo ng determinasyon at sikap siguradong payaman ka dito!\nAno pa hinihintay mo Friend? Titingin ka na lang ba dyan? Umaksyon ka na habang kaya mo pa! POW\u20ac\u20ac\u20ac\u20ac\u20acRRR heheheee.nakakatawa pero totoo..", + "story": "X-nhor Utto shared Tambayan OFW- international's photo.", + "story_tags": { + "0": [ + { + "id": "100003240641743", + "name": "X-nhor Utto", + "offset": 0, + "length": 11, + "type": "user" + } + ], + "19": [ + { + "id": "100624236677885", + "name": "Tambayan OFW- international", + "offset": 19, + "length": 27, + "type": "page" + } + ] + }, + "picture": "https://fbcdn-photos-b-a.akamaihd.net/hphotos-ak-frc3/969021_539010139505957_910074166_s.jpg", + "link": "https://www.facebook.com/photo.php?fbid=539010139505957&set=a.404265802980392.89616.100624236677885&type=1", + "name": "Timeline Photos", + "caption": "Im pretty sure maraming makakarelate dito. . .hahaha\n\n\n\nADMIN KYLIE", + "properties": [ + { + "name": "By", + "text": "Tambayan OFW- international", + "href": "https://www.facebook.com/tambayanofwinternational?ref=stream" + } + ], + "icon": "https://fbstatic-a.akamaihd.net/rsrc.php/v2/yD/r/aS8ecmYRys0.gif", + "privacy": { + "value": "" + }, + "type": "photo", + "object_id": "539010139505957", + "application": { + "name": "Facebook for Android", + "namespace": "fbandroid", + "id": "350685531728" + }, + "created_time": "2013-07-26T22:56:04+0000", + "updated_time": "2013-07-26T22:56:04+0000" + }, + { + "id": "1756587770_3252058117771", + "from": { + "name": "Benjamin Cabrera", + "id": "1756587770" + }, + "message": "BAKIT HINDI KA CRUSH NG CRUSH MO>>>>>>wahahah Trailer palaNg laughtriP nah!!!!", + "privacy": { + "value": "" + }, + "type": "status", + "created_time": "2013-07-26T22:55:56+0000", + "updated_time": "2013-07-26T22:55:56+0000" + }, + { + "id": "100000842245724_582493961788673", + "from": { + "name": "Rachel Rances Deang", + "id": "100000842245724" + }, + "message": "happy 99th anniv to all Iglesia Ni Cristo members and happy 92nd aniv local ng Caloocan... Praise God...Glorify Him to this wonderful moments and to all His Blessings to us...Hindi Niya Pinabayaan n mga Tapat na Lingkod Nya Kailanman...", + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Mobile", + "id": "186529194704290" + }, + "created_time": "2013-07-26T22:55:38+0000", + "updated_time": "2013-07-26T22:55:38+0000" + }, + { + "id": "100004589527871_205505996279050", + "from": { + "name": "Nhel Pellazo", + "id": "100004589527871" + }, + "message": "IKAW NA MANLOLOKO !]\n \n Haha wag tamaan ganyan tlga\n HINDI MAIWASAN KALANDIAN o KAKATIHAN NG ???? EWAN HAHA :D", + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Mobile", + "id": "2915120374" + }, + "created_time": "2013-07-26T22:55:37+0000", + "updated_time": "2013-07-26T22:55:37+0000" + }, + { + "id": "100001151882595_538033689578356", + "from": { + "name": "Nhorjannah Mansor Gumbay", + "id": "100001151882595" + }, + "message": "DAPAT SA ISANG RELASYUN AY HINDI GANTIHAN KAPAG NAGKAMALI KA\nDAPAT MATUTU KANG HUMINGI NG KAPATAWARAN AT MAPAGKUMBABA\nHINDI YUNG KONTING PROBLEMA LANG\nHAHALUNGKATIN MU LAHAT NG KASALANAN NIYA.......", + "privacy": { + "value": "" + }, + "type": "status", + "application": { + "name": "Mobile", + "id": "2915120374" + }, + "created_time": "2013-07-26T22:55:34+0000", + "updated_time": "2013-07-26T22:55:34+0000" + }, + { + "id": "445630152202723_446027772162961", + "from": { + "category": "Community", + "name": "Be\u015fir Sezgin", + "id": "445630152202723" + }, + "message": "\u00c7ocuk Dikmi\u015f Sa\u00e7Lar\u0131 Hindi Gibi ,\nGiymi\u015f ConverserLeri Birde K\u00fcpe Takm\u0131\u015f .\nTamam \u0130\u015fte 10 Numara Adam Daha Y\u00fcrek M\u00fcrek \u00d6nemLi De\u011fiL .\nK\u0131zLar Pe\u015finde Pervane 1-2 Ay Sonrada ALdat\u0131Ld\u0131m ,\nTerkediLdim , ErkekLer B\u00f6yLe ErkekLer \u015e\u00f6yLee\nEee K\u0131z\u0131m Sen Ka\u015f\u0131nd\u0131n Sen Adama De\u011fiL AksesuarLara Kap\u0131Ld\u0131n.\nSonrada Piyasada DeLikanL\u0131 Yok ki Deyip DoLand\u0131n !", + "actions": [ + { + "name": "Comment", + "link": "https://www.facebook.com/445630152202723/posts/446027772162961" + } + ], + "privacy": { + "value": "" + }, + "type": "status", + "created_time": "2013-07-26T22:55:32+0000", + "updated_time": "2013-07-26T22:55:32+0000" + } + ], + "paging": { + "previous": "https://graph.facebook.com/search?q=hindi&type=post&access_token=CAADysJa3mtcBADbzkQdT8LjgTNAJEZAZC36Ol7PwArlMIwLlDeDv1bn415WRGYIg3TCOlGMDpmtA7tWhFkSHZBuqtkUivghE7ZBtNSDabwFUt9hRZCeqA3uh5ttXBahQJAbCjrONcmfu9Kf4KQKUl&limit=25&since=1374879429&__previous=1", + "next": "https://graph.facebook.com/search?q=hindi&type=post&access_token=CAADysJa3mtcBADbzkQdT8LjgTNAJEZAZC36Ol7PwArlMIwLlDeDv1bn415WRGYIg3TCOlGMDpmtA7tWhFkSHZBuqtkUivghE7ZBtNSDabwFUt9hRZCeqA3uh5ttXBahQJAbCjrONcmfu9Kf4KQKUl&limit=25&until=1374879331" + } +} \ No newline at end of file diff --git a/tests/src/test/resources/jsonmergepatch.json b/tests/src/test/resources/jsonmergepatch.json new file mode 100644 index 00000000..3b90e5f5 --- /dev/null +++ b/tests/src/test/resources/jsonmergepatch.json @@ -0,0 +1,103 @@ +[ + { + "target": {}, + "patch": {"a": {"b":"c"}}, + "expected": {"a":{"b":"c"}} + }, + { + "target": {}, + "patch": {"a":{"bb":{"ccc":null}}}, + "expected": {"a":{"bb":{}}} + }, + { + "target": {"a":"foo"}, + "patch": "bar", + "expected": "bar" + }, + { + "target": {"a":"foo"}, + "patch": null, + "expected": null + }, + { + "target": {"a": {"b":"c"}}, + "patch": {"a": {"b":"d", "c":null}}, + "expected": {"a": {"b":"d"}} + }, + { + "target": { "c": "d" }, + "patch": { "a": "b" }, + "expected": { "a": "b", "c": "d" } + }, + { + "target": { "a": { "d": 2 } }, + "patch": { "a": { "d": 1 } }, + "expected": { "a": { "d": 1 } } + }, + { + "target": { "a": "b", "c": "d" }, + "patch": { "c": null }, + "expected": { "a": "b" } + }, + { + "target": { "a": { "b": "c", "d": null} }, + "patch": { "a": { "d": null} }, + "expected": { "a": { "b": "c" } } + }, + { + "target": { + "a": { "b": "c" }, + "d": "e" + }, + "patch": { + "a": 1000010002020389.8787987983 + }, + "expected": { + "a": 1000010002020389.8787987983, + "d": "e" + } + }, + { + "target": { "a": "b" }, + "patch": { "c": [ null ] }, + "expected": { "a": "b", "c": [ null ] } + }, + { + "target": { "a": { "b": null, "d": 3}, "e": -1 }, + "patch": { "a": { "b": "c", "d": null } }, + "expected": { "a": { "b": "c" }, "e": -1 } + }, + { + "target": [1,2], + "patch": { "a": "b", "c": null }, + "expected": { "a": "b"} + }, + { + "target": { + "title": "Goodbye!", + "author": { + "givenName": "John", + "familyName": "Doe" + }, + "tags": [ "example", "sample" ], + "content": "This will be unchanged" + }, + "patch": { + "title": "Hello!", + "phoneNumber": "+01-123-456-7890", + "author": { + "familyName": null + }, + "tags": [ "example" ] + }, + "expected": { + "title": "Hello!", + "author": { + "givenName": "John" + }, + "tags": [ "example" ], + "content": "This will be unchanged", + "phoneNumber": "+01-123-456-7890" + } + } +] \ No newline at end of file diff --git a/tests/src/test/resources/jsonmergepatchdiff.json b/tests/src/test/resources/jsonmergepatchdiff.json new file mode 100644 index 00000000..ac6c91de --- /dev/null +++ b/tests/src/test/resources/jsonmergepatchdiff.json @@ -0,0 +1,60 @@ +[ + { + "original": { + "title": "Goodbye!", + "author": { + "givenName": "John", + "familyName": "Doe" + }, + "tags": [ "example", "sample" ], + "content": "This will be unchanged" + }, + "expected": { + "title": "Hello!", + "phoneNumber": "+01-123-456-7890", + "author": { + "familyName": null + }, + "tags": [ "example" ] + }, + "target": { + "title": "Hello!", + "author": { + "givenName": "John" + }, + "tags": [ "example" ], + "content": "This will be unchanged", + "phoneNumber": "+01-123-456-7890" + } + }, + { + "original": {}, + "expected": {"a": {"b":"c"}}, + "target": {"a":{"b":"c"}} + }, + { + "original": {"a":"foo"}, + "expected": "bar", + "target": "bar" + }, + { + "original": {"a":"foo"}, + "expected": null, + "target": null + }, + { + "original": { "c": "d" }, + "expected": { "a": "b" }, + "target": { "a": "b", "c": "d" } + }, + { + "original": { "a": { "d": 2 } }, + "expected": { "a": { "d": 1 } }, + "target": { "a": { "d": 1 } } + }, + { + "original": { "a": "b", "c": "d" }, + "expected": { "c": null }, + "target": { "a": "b" } + } +] \ No newline at end of file diff --git a/tests/src/test/resources/jsonpatch.json b/tests/src/test/resources/jsonpatch.json new file mode 100644 index 00000000..785c82dd --- /dev/null +++ b/tests/src/test/resources/jsonpatch.json @@ -0,0 +1,219 @@ +[ + { + "op": { "op": "test", "path": "/a/1", "value": "hello" }, + "target": { "a": [ null, "hello", "world" ] }, + "expected": { "a": [ null, "hello", "world" ] } + }, + { + "op": { "op": "test", "path": "/x", "value": {} }, + "target": [ 1, 2 ], + "exception": "javax.json.JsonException" + }, + { + "op": { "op": "test", "path": "", "value": true }, + "target": [ 1, 2 ], + "exception": "javax.json.JsonException" + }, + { + "op": { "op": "test", "path": "/x", "value": -30.000 }, + "target": { "x": -29.020 }, + "exception": "javax.json.JsonException" + }, + { + "op": { "op": "replace", "path": "", "value": false }, + "target": { "x": { "a": "b", "y": {} } }, + "exception": "javax.json.JsonException" + }, + { + "op": { "op": "replace", "path": "/x/y", "value": "hello" }, + "target": { "x": { "a": "b", "y": {} } }, + "expected": { "x": { "a": "b", "y": "hello" } } + }, + { + "op": { "op": "replace", "path": "/0/2", "value": "x" }, + "target": [ [ "a", "b", "c"], "d", "e" ], + "expected": [ [ "a", "b", "x" ], "d", "e" ] + }, + { + "op": { "op": "replace", "path": "/x/0", "value": null }, + "target": { "x": [ "y", "z" ], "foo": "bar" }, + "expected": { "x": [ null, "z" ], "foo": "bar" } + }, + { + "op": { "op": "replace", "path": "/x/y", "value": 42 }, + "target": { "x": {} }, + "exception": "javax.json.JsonException" + }, + { + "op": { "op": "remove", "path": "/x/y" }, + "target": { "x": { "a": "b", "y": {} } }, + "expected": { "x": { "a": "b" } } + }, + { + "op": { "op": "remove", "path": "/0/2" }, + "target": [ [ "a", "b", "c"], "d", "e" ], + "expected": [ [ "a", "b" ], "d", "e" ] + }, + { + "op": { "op": "remove", "path": "/x/0" }, + "target": { "x": [ "y", "z" ], "foo": "bar" }, + "expected": { "x": [ "z" ], "foo": "bar" } + }, + { + "op": { "op": "remove", "path": "/x/y" }, + "target": { "x": {} }, + "exception": "javax.json.JsonException" + }, + { + "op": { "op": "move", "from": "/x/a", "path": "/x/b" }, + "target": { "x": { "a": "helo" } }, + "expected": { "x": { "b": "helo" } } + }, + { + "op": { "op": "move", "from": "/x/a", "path": "/x/a" }, + "target": { "x": { "a": "helo" } }, + "expected": { "x": { "a": "helo" } } + }, + { + "op": { "op": "move", "from": "/0", "path": "/0/x" }, + "target": [ "victim", {}, {} ], + "exception": "javax.json.JsonException" + }, + { + "op": { "op": "move", "from": "/0", "path": "/-" }, + "target": [ 0, 1, 2 ], + "expected": [ 1, 2, 0 ] + }, + { + "op": { "op": "move", "from": "/a", "path": "/b/2" }, + "target": { "a": "helo", "b": [ 1, 2, 3, 4 ] }, + "expected": { "b": [ 1, 2, "helo", 3, 4 ] } + }, + { + "op": { "op": "move", "from": "/a", "path": "/a/b" }, + "target": {}, + "exception": "javax.json.JsonException" + }, + { + "op": { "op": "move", "from": "/a", "path": "/b/c" }, + "target": { "a": "b" }, + "exception": "javax.json.JsonException" + }, + { + "op": { "op": "move", "from": "/x/a", "path": "/x/c" }, + "target": { "x": { "b": "helo" } }, + "exception": "javax.json.JsonException" + }, + { + "op": { "op": "copy", "from": "/a", "path": "/b" }, + "target": { "a": 1 }, + "expected": { "a": 1, "b": 1 } + }, + { + "op": { "op": "copy", "from": "/a", "path": "/b" }, + "target": { "a": 1, "b": false }, + "expected": { "a": 1, "b": 1 } + }, + { + "op": { "op": "copy", "from": "/0", "path": "/-" }, + "target": [ 1, 2, 3, 4 ], + "expected": [ 1, 2, 3, 4, 1 ] + }, + { + "op": { "op": "copy", "from": "/0", "path": "/0" }, + "target": [ true ], + "expected": [ true, true ] + }, + { + "op": { "op": "copy", "from": "/a", "path": "/b" }, + "target": {}, + "exception": "javax.json.JsonException" + }, + { + "op": { "op": "copy", "from": "/a", "path": "/b/c" }, + "target": { "a": 1 }, + "exception": "javax.json.JsonException" + }, + { + "op": { "op": "add", "path": "/a/b/c", "value": 1 }, + "target": { "a": "b" }, + "exception": "javax.json.JsonException" + }, + { + "op": { "op": "add", "path": "/~1", "value": 1 }, + "target": [], + "exception": "javax.json.JsonException" + }, + { + "op": { "op": "add", "path": "/3", "value": 1 }, + "target": [ 1, 2 ], + "exception": "javax.json.JsonException" + }, + { + "op": { "op": "add", "path": "/-2", "value": 1 }, + "target": [ 1, 2 ], + "exception": "javax.json.JsonException" + }, + { + "op": { "op": "add", "path": "/foo/f", "value": "bar" }, + "target": { "foo": "bar" }, + "exception": "javax.json.JsonException" + }, + { + "op": { "op": "add", "path": "/a", "value": "b" }, + "target": {}, + "expected": { "a": "b" } + }, + { + "op": { "op": "add", "path": "/a", "value": 1 }, + "target": { "a": "b" }, + "expected": { "a": 1 } + }, + { + "op": { "op": "add", "path": "/array/-", "value": 1 }, + "target": { "array": [ 2, null, {}, 1 ] }, + "expected": { "array": [ 2, null, {}, 1, 1 ] } + }, + { + "op": { "op": "add", "path": "/array/2", "value": "hello" }, + "target": { "array": [ 2, null, {}, 1] }, + "expected": { "array": [ 2, null, "hello", {}, 1 ] } + }, + { + "op": { "op": "add", "path": "/obj/inner/b", "value": [ 1, 2 ] }, + "target": { + "obj": { + "inner": { + "a": "hello" + } + } + }, + "expected": { + "obj": { + "inner": { + "a": "hello", + "b": [ 1, 2 ] + } + } + } + }, + { + "op": { "op": "add", "path": "/obj/inner/b", "value": [ 1, 2 ] }, + "target": { + "obj": { + "inner": { + "a": "hello", + "b": "world" + } + } + }, + "expected": { + "obj": { + "inner": { + "a": "hello", + "b": [ 1, 2 ] + } + } + } + } +] diff --git a/tests/src/test/resources/jsonpatchdiff.json b/tests/src/test/resources/jsonpatchdiff.json new file mode 100644 index 00000000..529b79ec --- /dev/null +++ b/tests/src/test/resources/jsonpatchdiff.json @@ -0,0 +1,160 @@ +[ + { + "original": {"a":"b"}, + "target": {"a":"b"}, + "expected": [] + }, + { + "original": [ 1, 2, 3 ], + "target": [ 1, 2, 3, 4, 5 ], + "expected": [ + {"op":"add","path":"/3","value":4}, + {"op":"add","path":"/4","value":5} + ] + }, + { + "original": [1,2,3,4,5], + "target": [1,3,4], + "expected": [ + { "op": "remove", "path": "/4"}, + { "op": "remove", "path": "/1"} + ] + }, + { + "original": [1,2,3,4,5,6], + "target": [1,7,3,4,8,5], + "expected": [ + { "op": "remove", "path": "/5"}, + { "op": "replace", "path": "/1", "value": 7}, + { "op": "add", "path": "/4", "value": 8} + ] + }, + { + "original": [ 1, 2, 3 ], + "target": [ 1 ], + "expected": [ + { "op": "remove", "path": "/2" }, + { "op": "remove", "path": "/1" } + ] + }, + { + "original": { "a": "b", "c": "d" }, + "target": { "a": "b" }, + "expected": [ + { "op": "remove", "path": "/c" } + ] + }, + { + "original": { "a": 1 }, + "target": { "a": 1, "c": 2, "b": 3, "d": 4 }, + "expected": [ + { "op": "add", "path": "/c", "value": 2 }, + { "op": "add", "path": "/b", "value": 3 }, + { "op": "add", "path": "/d", "value": 4 } + ] + }, + { + "original": { "a": null }, + "target": { "a": 6 }, + "expected": [ + { "op": "replace", "path": "/a", "value": 6 } + ] + }, + { + "original": [ 1, 2, 3 ], + "target": { "hello": "world" }, + "expected": [ + { "op": "replace", "path": "", "value": { "hello": "world" } } + ] + }, + { + "original": { + "a": "b", + "c": { + "d": "e" + } + }, + "target": { + "a": "b", + "c": { + "d": 1, + "e": "f" + } + }, + "expected": [ + { "op": "replace", "path": "/c/d", "value": 1 }, + { "op": "add", "path": "/c/e", "value": "f" } + ] + }, + { + "original": { + "a": [ 1, 2, 3 ] + }, + "target": { + "a": [ "b", 2, 3, 4 ] + }, + "expected": [ + { "op": "replace", "path": "/a/0", "value":"b" }, + { "op": "add", "path": "/a/3", "value":4 } + ] + }, + { + "original": [ { "a": "b" }, "foo", { "bar": null } ], + "target": [ { "a": "b", "c": "d" }, "foo", { "bar": "baz" } ], + "expected": [ + { "op": "replace", "path": "/2/bar", "value": "baz" }, + { "op": "add", "path": "/0/c", "value": "d" } + ] + }, + { + "original": [ 1, [ 2, 3 ], 4 ], + "target": [ "x", [ 2, 3, "y" ], 4 ], + "expected": [ + { "op": "add", "path": "/1/2", "value": "y" }, + { "op": "replace", "path": "/0", "value": "x" } + ] + }, + { + "original": { "a": "b" }, + "target": { "c": "b" }, + "expected": [ + { "op": "remove", "path": "/a"}, + { "op": "add", "path": "/c", "value": "b"} + ] + }, + { + "original": {"a": "c"}, + "target": {"a": "c", "d": "c"}, + "expected": [ + { "op": "add", "path": "/d", "value": "c" } + ] + }, + { + "original": [-1, 0, 1, 3, 4], + "target": [5, 0], + "expected": [ + { "path" : "/4", "op" : "remove"}, + { "path" : "/3", "op" : "remove"}, + { "path" : "/2", "op" : "remove"}, + { "value" : 5, "path" : "/0", "op" : "replace" } + ] + }, + { + "original": [0], + "target": [0, 1, 2, 3, 4], + "expected": [ + { "path" : "/1", "value" : 1, "op" : "add" }, + { "path" : "/2", "value" : 2, "op" : "add" }, + { "value" : 3, "path" : "/3", "op" : "add" }, + { "op" : "add", "path" : "/4", "value" : 4 } + ] + }, + { + "original": [0, 2, 4], + "target": [0, 1, 2, 3, 4], + "expected": [ + { "path" : "/1", "value" : 1, "op" : "add" }, + { "value" : 3, "op" : "add", "path" : "/3" } + ] + } +] diff --git a/tests/src/test/resources/rfc6901.json b/tests/src/test/resources/rfc6901.json new file mode 100644 index 00000000..170147b7 --- /dev/null +++ b/tests/src/test/resources/rfc6901.json @@ -0,0 +1,20 @@ + { + "foo": ["bar", "baz"], + "": 0, + "a/b": 1, + "c%d": 2, + "e^f": 3, + "g|h": 4, + "i\\j": 5, + "k\"l": 6, + " ": 7, + "m~n": 8, + "o" : null, + "p": { + "q":"r" + }, + "s": [ { + "t":"u" + } + ] + } \ No newline at end of file diff --git a/tests/src/test/resources/twitter.json b/tests/src/test/resources/twitter.json new file mode 100644 index 00000000..677f2b91 --- /dev/null +++ b/tests/src/test/resources/twitter.json @@ -0,0 +1,2 @@ +{"statuses":[{"metadata":{"result_type":"recent","iso_language_code":"de"},"created_at":"Fri Jul 26 19:31:26 +0000 2013","id":360844831439855616,"id_str":"360844831439855616","text":"Hiring ••► Pre-Sales Consultant ☛ http://t.co/fjlOd8PhcQ #Java #JUnit #Python #PHP #XML #XSLT #SOAP #REST #SAML #Node","source":"SocialOomph","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":93646439,"id_str":"93646439","name":"Paul Pleus","screen_name":"IER_Recruiting","location":"Worldwide","description":"International Executive Recruiting – IER, Xing, Linkedin, Recruiting, Recruiter, Headhunter Personalberater, Job, Jobs, Stellenangebot, Manager","url":"http://t.co/GtcxFsUyI3","entities":{"url":{"urls":[{"url":"http://t.co/GtcxFsUyI3","expanded_url":"http://www.ier-network.com","display_url":"ier-network.com","indices":[0,22]}]},"description":{"urls":[]}},"protected":false,"followers_count":17822,"friends_count":15829,"listed_count":119,"created_at":"Mon Nov 30 15:02:59 +0000 2009","favourites_count":0,"utc_offset":null,"time_zone":null,"geo_enabled":false,"verified":false,"statuses_count":26733,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"0099B9","profile_background_image_url":"http://a0.twimg.com/profile_background_images/216930247/pleus_final_logo_KUGEL.jpg","profile_background_image_url_https":"https://si0.twimg.com/profile_background_images/216930247/pleus_final_logo_KUGEL.jpg","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/1270812893/Final_198x146_normal.jpg","profile_image_url_https":"https://si0.twimg.com/profile_images/1270812893/Final_198x146_normal.jpg","profile_link_color":"0099B9","profile_sidebar_border_color":"5ED4DC","profile_sidebar_fill_color":"95E8EC","profile_text_color":"3C3940","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[{"text":"Java","indices":[57,62]},{"text":"JUnit","indices":[63,69]},{"text":"Python","indices":[70,77]},{"text":"PHP","indices":[78,82]},{"text":"XML","indices":[83,87]},{"text":"XSLT","indices":[88,93]},{"text":"SOAP","indices":[94,99]},{"text":"REST","indices":[100,105]},{"text":"SAML","indices":[106,111]},{"text":"Node","indices":[112,117]}],"symbols":[],"urls":[{"url":"http://t.co/fjlOd8PhcQ","expanded_url":"http://bit.ly/19I4P7E","display_url":"bit.ly/19I4P7E","indices":[34,56]}],"user_mentions":[]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"de"},{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Jul 26 19:30:39 +0000 2013","id":360844632520798209,"id_str":"360844632520798209","text":"Death toll reaches 15 for #Indonesian asylum seeker boat that capsized off the coast of west #Java http://t.co/4rGk0LO9F8","source":"TweetDeck","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":90407341,"id_str":"90407341","name":"CSIS Southeast Asia","screen_name":"SoutheastAsiaDC","location":"Washington, DC","description":"The Sumitro Chair for Southeast Asia Studies @CSIS is the premier forum for sustained elevated policy dialogue on Southeast Asia and US interests in the region.","url":"http://t.co/1bcQU6sG1V","entities":{"url":{"urls":[{"url":"http://t.co/1bcQU6sG1V","expanded_url":"http://bit.ly/csis-seap","display_url":"bit.ly/csis-seap","indices":[0,22]}]},"description":{"urls":[]}},"protected":false,"followers_count":8127,"friends_count":523,"listed_count":427,"created_at":"Mon Nov 16 14:51:00 +0000 2009","favourites_count":16,"utc_offset":-14400,"time_zone":"Eastern Time (US & Canada)","geo_enabled":false,"verified":false,"statuses_count":12019,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"B2DFDA","profile_background_image_url":"http://a0.twimg.com/profile_background_images/669153955/be1298700d512cfdd3734a677a587095.gif","profile_background_image_url_https":"https://si0.twimg.com/profile_background_images/669153955/be1298700d512cfdd3734a677a587095.gif","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/2648645038/e06eb649efb4030845d4cbf47d41651b_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/2648645038/e06eb649efb4030845d4cbf47d41651b_normal.jpeg","profile_banner_url":"https://pbs.twimg.com/profile_banners/90407341/1371826204","profile_link_color":"93A644","profile_sidebar_border_color":"FFFFFF","profile_sidebar_fill_color":"FFFFFF","profile_text_color":"333333","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[{"text":"Indonesian","indices":[26,37]},{"text":"Java","indices":[93,98]}],"symbols":[],"urls":[{"url":"http://t.co/4rGk0LO9F8","expanded_url":"http://fxn.ws/1c7VZ2F","display_url":"fxn.ws/1c7VZ2F","indices":[99,121]}],"user_mentions":[]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"en"},{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Jul 26 19:30:15 +0000 2013","id":360844531593261057,"id_str":"360844531593261057","text":"Senior Java Developer w/ #Java #Software skills Addison @p2people http://t.co/Fc9UlSdyWj","source":"p2people","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":551581457,"id_str":"551581457","name":"p2p WebMobileIT","screen_name":"p2pWebMobileIt","location":"Stevenage | Hertfordshire","description":"Web, Mobile & IT micro #outsourcing and #freelance jobs at p2people","url":"http://t.co/OxLhyJdlxw","entities":{"url":{"urls":[{"url":"http://t.co/OxLhyJdlxw","expanded_url":"http://www.p2people.co.uk","display_url":"p2people.co.uk","indices":[0,22]}]},"description":{"urls":[]}},"protected":false,"followers_count":712,"friends_count":3,"listed_count":44,"created_at":"Thu Apr 12 05:07:17 +0000 2012","favourites_count":0,"utc_offset":3600,"time_zone":"London","geo_enabled":false,"verified":false,"statuses_count":88202,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"C0DEED","profile_background_image_url":"http://a0.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme1/bg.png","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/2099631495/p2people_logo_48x48_normal.png","profile_image_url_https":"https://si0.twimg.com/profile_images/2099631495/p2people_logo_48x48_normal.png","profile_link_color":"0084B4","profile_sidebar_border_color":"C0DEED","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":true,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[{"text":"Java","indices":[25,30]},{"text":"Software","indices":[31,40]}],"symbols":[],"urls":[{"url":"http://t.co/Fc9UlSdyWj","expanded_url":"http://bit.ly/175ch6w","display_url":"bit.ly/175ch6w","indices":[66,88]}],"user_mentions":[{"screen_name":"p2people","name":"p2people","id":22741835,"id_str":"22741835","indices":[56,65]}]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"en"},{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Jul 26 19:30:00 +0000 2013","id":360844471761502209,"id_str":"360844471761502209","text":"Took 658ms to processing 200 tweets #JAVA #APPENGINE #1374867000804","source":"You Can't Be Wrong","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":1186398894,"id_str":"1186398894","name":"You Can't Be Wrong","screen_name":"YouCantBeWrong","location":"@tegaralaga's mind","description":"User generated content text game based on Twitter. For more information send your email to tegaralaga(at)live(dot)com","url":"http://t.co/Jto68Xkf","entities":{"url":{"urls":[{"url":"http://t.co/Jto68Xkf","expanded_url":"http://google.co.id/","display_url":"google.co.id","indices":[0,20]}]},"description":{"urls":[]}},"protected":false,"followers_count":8,"friends_count":1,"listed_count":1,"created_at":"Sat Feb 16 14:30:21 +0000 2013","favourites_count":0,"utc_offset":null,"time_zone":null,"geo_enabled":false,"verified":false,"statuses_count":22815,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"C0DEED","profile_background_image_url":"http://a0.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme1/bg.png","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/3261474447/f3cabcc48d277be37e45ac7b1038d73e_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/3261474447/f3cabcc48d277be37e45ac7b1038d73e_normal.jpeg","profile_banner_url":"https://pbs.twimg.com/profile_banners/1186398894/1361117710","profile_link_color":"0084B4","profile_sidebar_border_color":"C0DEED","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":true,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[{"text":"JAVA","indices":[36,41]},{"text":"APPENGINE","indices":[42,52]}],"symbols":[],"urls":[],"user_mentions":[]},"favorited":false,"retweeted":false,"lang":"en"},{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Jul 26 19:29:22 +0000 2013","id":360844310297591811,"id_str":"360844310297591811","text":"RT @ECBader: Some interesting open source #Java projects from #Esri http://t.co/1pvAEzkV9r. Geometry API is worth checking out! https://t.c…","source":"Twitter for iPhone","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":11640022,"id_str":"11640022","name":"Philip Heede","screen_name":"pheede","location":"Redlands, CA","description":"Transplanted Dane using GIS at Esri w/.NET/XAML/Geoprocessing/Python/ArcObjects/Network Analyst/.. Disclaimer: all opinions, comments are my own, blah blah..","url":null,"entities":{"description":{"urls":[]}},"protected":false,"followers_count":355,"friends_count":267,"listed_count":22,"created_at":"Sat Dec 29 20:25:10 +0000 2007","favourites_count":114,"utc_offset":-25200,"time_zone":"Pacific Time (US & Canada)","geo_enabled":true,"verified":false,"statuses_count":1711,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"C0DEED","profile_background_image_url":"http://a0.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme1/bg.png","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/335064929/n701240139_383204_9480_normal.jpg","profile_image_url_https":"https://si0.twimg.com/profile_images/335064929/n701240139_383204_9480_normal.jpg","profile_link_color":"0084B4","profile_sidebar_border_color":"C0DEED","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":true,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweeted_status":{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Jul 26 15:40:13 +0000 2013","id":360786645261352961,"id_str":"360786645261352961","text":"Some interesting open source #Java projects from #Esri http://t.co/1pvAEzkV9r. Geometry API is worth checking out! https://t.co/yEXKNTClKZ","source":"TweetDeck","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":26422164,"id_str":"26422164","name":"Eric Bader","screen_name":"ECBader","location":"Southern California","description":"Geographer, Musician, Christ-follower, Java dude, Nebraska Cornhusker, Esri Product Management Team - ArcGIS.","url":null,"entities":{"description":{"urls":[]}},"protected":false,"followers_count":614,"friends_count":473,"listed_count":37,"created_at":"Wed Mar 25 03:44:15 +0000 2009","favourites_count":27,"utc_offset":-25200,"time_zone":"Pacific Time (US & Canada)","geo_enabled":true,"verified":false,"statuses_count":2700,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"1A1B1F","profile_background_image_url":"http://a0.twimg.com/images/themes/theme9/bg.gif","profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme9/bg.gif","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/2586630286/dct7znna7xosntblt7jb_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/2586630286/dct7znna7xosntblt7jb_normal.jpeg","profile_link_color":"2FC2EF","profile_sidebar_border_color":"181A1E","profile_sidebar_fill_color":"252429","profile_text_color":"666666","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":2,"favorite_count":0,"entities":{"hashtags":[{"text":"Java","indices":[29,34]},{"text":"Esri","indices":[49,54]}],"symbols":[],"urls":[{"url":"http://t.co/1pvAEzkV9r","expanded_url":"http://esri.github.io/#Java","display_url":"esri.github.io/#Java","indices":[55,77]},{"url":"https://t.co/yEXKNTClKZ","expanded_url":"https://github.com/Esri/geometry-api-java","display_url":"github.com/Esri/geometry-…","indices":[115,138]}],"user_mentions":[]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"en"},"retweet_count":2,"favorite_count":0,"entities":{"hashtags":[{"text":"Java","indices":[42,47]},{"text":"Esri","indices":[62,67]}],"symbols":[],"urls":[{"url":"http://t.co/1pvAEzkV9r","expanded_url":"http://esri.github.io/#Java","display_url":"esri.github.io/#Java","indices":[68,90]}],"user_mentions":[{"screen_name":"ECBader","name":"Eric Bader","id":26422164,"id_str":"26422164","indices":[3,11]}]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"en"},{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Jul 26 19:29:17 +0000 2013","id":360844289841971200,"id_str":"360844289841971200","text":"Looking for a new IT challenge? Take a look at our job openings http://t.co/MhnnR101cT #atlassian #java #oracle #mobile #infra #jobs","source":"TweetDeck","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":296655269,"id_str":"296655269","name":"Koen Gillard","screen_name":"KoenGillard","location":"","description":"Sr Java/Oracle Consultant at @Contribute4J. Certified Scrum Master. Big Atlassian enthusiast. Father of 2 great sons. Other interests music/soccer/cycling/...","url":null,"entities":{"description":{"urls":[]}},"protected":false,"followers_count":151,"friends_count":341,"listed_count":4,"created_at":"Wed May 11 05:23:43 +0000 2011","favourites_count":874,"utc_offset":7200,"time_zone":"Brussels","geo_enabled":false,"verified":false,"statuses_count":549,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"ACDED6","profile_background_image_url":"http://a0.twimg.com/images/themes/theme18/bg.gif","profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme18/bg.gif","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/3690118960/3bf92c8b7f1398f35e635f0d08bde368_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/3690118960/3bf92c8b7f1398f35e635f0d08bde368_normal.jpeg","profile_link_color":"038543","profile_sidebar_border_color":"EEEEEE","profile_sidebar_fill_color":"F6F6F6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[{"text":"atlassian","indices":[87,97]},{"text":"java","indices":[98,103]},{"text":"oracle","indices":[104,111]},{"text":"mobile","indices":[112,119]},{"text":"infra","indices":[120,126]},{"text":"jobs","indices":[127,132]}],"symbols":[],"urls":[{"url":"http://t.co/MhnnR101cT","expanded_url":"http://jobs.contribute.be","display_url":"jobs.contribute.be","indices":[64,86]}],"user_mentions":[]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"en"},{"metadata":{"result_type":"recent","iso_language_code":"in"},"created_at":"Fri Jul 26 19:27:56 +0000 2013","id":360843950518566914,"id_str":"360843950518566914","text":"RT @SKSMediaMalay: Rip-off or investor's dream? You decide...http://t.co/bxk76YXRJM #Bandar #Lampung #java #Sumatra #Banten #Riau #Padang //","source":"RoundTeam","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":1596343046,"id_str":"1596343046","name":"Rooms_In_Lowestoft","screen_name":"RoomsLowestoft","location":"Lowestoft, Suffolk, UK","description":"#Rooms #Room to #rent #let in #Lowestoft, #Suffolk, #UK E: lowestoft@niche.me.uk \r\n\r\n#norfolk #greatyarmouth #saxmundham #ipswich #aldeburgh #leiston #tfbuk","url":"https://t.co/aeCwVfy3l0","entities":{"url":{"urls":[{"url":"https://t.co/aeCwVfy3l0","expanded_url":"https://twitter.com/RoomsLowestoft","display_url":"twitter.com/RoomsLowestoft","indices":[0,23]}]},"description":{"urls":[]}},"protected":false,"followers_count":963,"friends_count":1742,"listed_count":14,"created_at":"Mon Jul 15 17:23:58 +0000 2013","favourites_count":182,"utc_offset":null,"time_zone":null,"geo_enabled":false,"verified":false,"statuses_count":9095,"lang":"en-gb","contributors_enabled":false,"is_translator":false,"profile_background_color":"C0DEED","profile_background_image_url":"http://a0.twimg.com/profile_background_images/378800000024425110/a2acf9f2d9bf8f8c90cb24910a71c037.jpeg","profile_background_image_url_https":"https://si0.twimg.com/profile_background_images/378800000024425110/a2acf9f2d9bf8f8c90cb24910a71c037.jpeg","profile_background_tile":true,"profile_image_url":"http://a0.twimg.com/profile_images/378800000138549193/b276cf4d94b9c96e5b61c92f9864615f_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/378800000138549193/b276cf4d94b9c96e5b61c92f9864615f_normal.jpeg","profile_banner_url":"https://pbs.twimg.com/profile_banners/1596343046/1373917379","profile_link_color":"0084B4","profile_sidebar_border_color":"FFFFFF","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweeted_status":{"metadata":{"result_type":"recent","iso_language_code":"in"},"created_at":"Fri Jul 26 19:20:50 +0000 2013","id":360842165292773376,"id_str":"360842165292773376","text":"Rip-off or investor's dream? You decide...http://t.co/bxk76YXRJM #Bandar #Lampung #java #Sumatra #Banten #Riau #Padang //","source":"HootSuite","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":1449573356,"id_str":"1449573356","name":"SKS Media Malaysia","screen_name":"SKSMediaMalay","location":"Kuala Lumpur","description":"Pemasaran syarikat perkhidmatan.\r\n\r\n#teamfollowback #tfb #tfbjp #autofollow #followback","url":"https://t.co/nfmdo6g6HC","entities":{"url":{"urls":[{"url":"https://t.co/nfmdo6g6HC","expanded_url":"https://twitter.com/SKSMediaMalay","display_url":"twitter.com/SKSMediaMalay","indices":[0,23]}]},"description":{"urls":[]}},"protected":false,"followers_count":1048,"friends_count":877,"listed_count":27,"created_at":"Wed May 22 18:07:31 +0000 2013","favourites_count":166,"utc_offset":null,"time_zone":null,"geo_enabled":false,"verified":false,"statuses_count":8006,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"C0DEED","profile_background_image_url":"http://a0.twimg.com/profile_background_images/874883247/77db35ff9cff8c42d2ef4c2bcd22df35.jpeg","profile_background_image_url_https":"https://si0.twimg.com/profile_background_images/874883247/77db35ff9cff8c42d2ef4c2bcd22df35.jpeg","profile_background_tile":true,"profile_image_url":"http://a0.twimg.com/profile_images/3695357949/77db35ff9cff8c42d2ef4c2bcd22df35_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/3695357949/77db35ff9cff8c42d2ef4c2bcd22df35_normal.jpeg","profile_link_color":"0084B4","profile_sidebar_border_color":"FFFFFF","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":1,"favorite_count":0,"entities":{"hashtags":[{"text":"Bandar","indices":[65,72]},{"text":"Lampung","indices":[73,81]},{"text":"java","indices":[82,87]},{"text":"Sumatra","indices":[88,96]},{"text":"Banten","indices":[97,104]},{"text":"Riau","indices":[105,110]},{"text":"Padang","indices":[111,118]}],"symbols":[],"urls":[{"url":"http://t.co/bxk76YXRJM","expanded_url":"http://tinyurl.com/dxxrao5","display_url":"tinyurl.com/dxxrao5","indices":[42,64]}],"user_mentions":[]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"in"},"retweet_count":1,"favorite_count":0,"entities":{"hashtags":[{"text":"Bandar","indices":[84,91]},{"text":"Lampung","indices":[92,100]},{"text":"java","indices":[101,106]},{"text":"Sumatra","indices":[107,115]},{"text":"Banten","indices":[116,123]},{"text":"Riau","indices":[124,129]},{"text":"Padang","indices":[130,137]}],"symbols":[],"urls":[{"url":"http://t.co/bxk76YXRJM","expanded_url":"http://tinyurl.com/dxxrao5","display_url":"tinyurl.com/dxxrao5","indices":[61,83]}],"user_mentions":[{"screen_name":"SKSMediaMalay","name":"SKS Media Malaysia","id":1449573356,"id_str":"1449573356","indices":[3,17]}]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"in"},{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Jul 26 19:25:27 +0000 2013","id":360843324011184128,"id_str":"360843324011184128","text":"#Java #Developer #BackEnd position open in #Dallas TX. #BED #BE #JavaDev Apply Online Today! - http://t.co/oVDyhoR65M","source":"HootSuite","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":52097984,"id_str":"52097984","name":"InSource Group","screen_name":"InSourcegroup","location":"Dallas, Texas","description":"Technical Resource & Contract Staffing Provider with offices in Dallas, Fort Worth, and Houston.","url":"http://t.co/IyUTPyeCMm","entities":{"url":{"urls":[{"url":"http://t.co/IyUTPyeCMm","expanded_url":"http://www.insourcegroup.com","display_url":"insourcegroup.com","indices":[0,22]}]},"description":{"urls":[]}},"protected":false,"followers_count":1712,"friends_count":1899,"listed_count":63,"created_at":"Mon Jun 29 16:03:36 +0000 2009","favourites_count":81,"utc_offset":-18000,"time_zone":"Central Time (US & Canada)","geo_enabled":false,"verified":false,"statuses_count":43289,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"FFFFFF","profile_background_image_url":"http://a0.twimg.com/profile_background_images/57804915/nvoffice.br.jpg","profile_background_image_url_https":"https://si0.twimg.com/profile_background_images/57804915/nvoffice.br.jpg","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/1109935707/insource-fb_normal.gif","profile_image_url_https":"https://si0.twimg.com/profile_images/1109935707/insource-fb_normal.gif","profile_banner_url":"https://pbs.twimg.com/profile_banners/52097984/1369854759","profile_link_color":"0083FF","profile_sidebar_border_color":"000000","profile_sidebar_fill_color":"7ABAE6","profile_text_color":"000000","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[{"text":"Java","indices":[0,5]},{"text":"Developer","indices":[6,16]},{"text":"BackEnd","indices":[17,25]},{"text":"Dallas","indices":[43,50]},{"text":"BED","indices":[55,59]},{"text":"BE","indices":[60,63]},{"text":"JavaDev","indices":[64,72]}],"symbols":[],"urls":[{"url":"http://t.co/oVDyhoR65M","expanded_url":"http://ow.ly/nkcSS","display_url":"ow.ly/nkcSS","indices":[95,117]}],"user_mentions":[]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"en"},{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Jul 26 19:22:18 +0000 2013","id":360842533410045954,"id_str":"360842533410045954","text":"3,2,1... Stir and energy will be served! #coffee #addict #givemecaffine #java http://t.co/OjGso3pbPt","source":"Instagram","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":17855557,"id_str":"17855557","name":"Heather Cereghino","screen_name":"HeatherCinOC","location":"Orange County, CA","description":"Social media-lite, creative writing (beach + laptop = bliss), Event Planner, PR, cook in the making, love the outdoors, traveling & shoes. PS. I love shoes!","url":null,"entities":{"description":{"urls":[]}},"protected":false,"followers_count":4114,"friends_count":4410,"listed_count":232,"created_at":"Thu Dec 04 01:19:51 +0000 2008","favourites_count":218,"utc_offset":-25200,"time_zone":"Pacific Time (US & Canada)","geo_enabled":true,"verified":false,"statuses_count":12505,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"B2DFDA","profile_background_image_url":"http://a0.twimg.com/images/themes/theme13/bg.gif","profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme13/bg.gif","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/3673021821/693873aafc52e32076ff85d68302ff78_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/3673021821/693873aafc52e32076ff85d68302ff78_normal.jpeg","profile_banner_url":"https://pbs.twimg.com/profile_banners/17855557/1370094052","profile_link_color":"93A644","profile_sidebar_border_color":"EEEEEE","profile_sidebar_fill_color":"FFFFFF","profile_text_color":"333333","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[{"text":"coffee","indices":[41,48]},{"text":"addict","indices":[49,56]},{"text":"givemecaffine","indices":[57,71]},{"text":"java","indices":[72,77]}],"symbols":[],"urls":[{"url":"http://t.co/OjGso3pbPt","expanded_url":"http://instagram.com/p/cPZ9yEqwlf/","display_url":"instagram.com/p/cPZ9yEqwlf/","indices":[78,100]}],"user_mentions":[]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"en"},{"metadata":{"result_type":"recent","iso_language_code":"in"},"created_at":"Fri Jul 26 19:20:50 +0000 2013","id":360842165292773376,"id_str":"360842165292773376","text":"Rip-off or investor's dream? You decide...http://t.co/bxk76YXRJM #Bandar #Lampung #java #Sumatra #Banten #Riau #Padang //","source":"HootSuite","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":1449573356,"id_str":"1449573356","name":"SKS Media Malaysia","screen_name":"SKSMediaMalay","location":"Kuala Lumpur","description":"Pemasaran syarikat perkhidmatan.\r\n\r\n#teamfollowback #tfb #tfbjp #autofollow #followback","url":"https://t.co/nfmdo6g6HC","entities":{"url":{"urls":[{"url":"https://t.co/nfmdo6g6HC","expanded_url":"https://twitter.com/SKSMediaMalay","display_url":"twitter.com/SKSMediaMalay","indices":[0,23]}]},"description":{"urls":[]}},"protected":false,"followers_count":1048,"friends_count":877,"listed_count":27,"created_at":"Wed May 22 18:07:31 +0000 2013","favourites_count":166,"utc_offset":null,"time_zone":null,"geo_enabled":false,"verified":false,"statuses_count":8006,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"C0DEED","profile_background_image_url":"http://a0.twimg.com/profile_background_images/874883247/77db35ff9cff8c42d2ef4c2bcd22df35.jpeg","profile_background_image_url_https":"https://si0.twimg.com/profile_background_images/874883247/77db35ff9cff8c42d2ef4c2bcd22df35.jpeg","profile_background_tile":true,"profile_image_url":"http://a0.twimg.com/profile_images/3695357949/77db35ff9cff8c42d2ef4c2bcd22df35_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/3695357949/77db35ff9cff8c42d2ef4c2bcd22df35_normal.jpeg","profile_link_color":"0084B4","profile_sidebar_border_color":"FFFFFF","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":1,"favorite_count":0,"entities":{"hashtags":[{"text":"Bandar","indices":[65,72]},{"text":"Lampung","indices":[73,81]},{"text":"java","indices":[82,87]},{"text":"Sumatra","indices":[88,96]},{"text":"Banten","indices":[97,104]},{"text":"Riau","indices":[105,110]},{"text":"Padang","indices":[111,118]}],"symbols":[],"urls":[{"url":"http://t.co/bxk76YXRJM","expanded_url":"http://tinyurl.com/dxxrao5","display_url":"tinyurl.com/dxxrao5","indices":[42,64]}],"user_mentions":[]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"in"},{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Jul 26 19:20:08 +0000 2013","id":360841988431556609,"id_str":"360841988431556609","text":"Took 8397ms to processing 200 tweets #JAVA #APPENGINE #1374866408704","source":"You Can't Be Wrong","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":1186398894,"id_str":"1186398894","name":"You Can't Be Wrong","screen_name":"YouCantBeWrong","location":"@tegaralaga's mind","description":"User generated content text game based on Twitter. For more information send your email to tegaralaga(at)live(dot)com","url":"http://t.co/Jto68Xkf","entities":{"url":{"urls":[{"url":"http://t.co/Jto68Xkf","expanded_url":"http://google.co.id/","display_url":"google.co.id","indices":[0,20]}]},"description":{"urls":[]}},"protected":false,"followers_count":8,"friends_count":1,"listed_count":1,"created_at":"Sat Feb 16 14:30:21 +0000 2013","favourites_count":0,"utc_offset":null,"time_zone":null,"geo_enabled":false,"verified":false,"statuses_count":22815,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"C0DEED","profile_background_image_url":"http://a0.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme1/bg.png","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/3261474447/f3cabcc48d277be37e45ac7b1038d73e_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/3261474447/f3cabcc48d277be37e45ac7b1038d73e_normal.jpeg","profile_banner_url":"https://pbs.twimg.com/profile_banners/1186398894/1361117710","profile_link_color":"0084B4","profile_sidebar_border_color":"C0DEED","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":true,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[{"text":"JAVA","indices":[37,42]},{"text":"APPENGINE","indices":[43,53]}],"symbols":[],"urls":[],"user_mentions":[]},"favorited":false,"retweeted":false,"lang":"en"},{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Jul 26 19:18:09 +0000 2013","id":360841487262547968,"id_str":"360841487262547968","text":"Can’t decide between more #java or #c++","source":"Tweetbot for iOS","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":29063267,"id_str":"29063267","name":"Steven Truesdell","screen_name":"TrueSteve","location":"Denver, CO","description":"Snowboarder. Photographer. Web Designer. Journalist. Music lover. Soon to be Firefighter & EMT","url":"http://t.co/fCPFLstB49","entities":{"url":{"urls":[{"url":"http://t.co/fCPFLstB49","expanded_url":"http://www.steventruesdell.com","display_url":"steventruesdell.com","indices":[0,22]}]},"description":{"urls":[]}},"protected":false,"followers_count":88,"friends_count":144,"listed_count":3,"created_at":"Sun Apr 05 21:02:17 +0000 2009","favourites_count":0,"utc_offset":-21600,"time_zone":"Mountain Time (US & Canada)","geo_enabled":true,"verified":false,"statuses_count":1792,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"1A1B1F","profile_background_image_url":"http://a0.twimg.com/images/themes/theme9/bg.gif","profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme9/bg.gif","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/3637401835/e96f6955cd6bfd3d7bf78002ebdbd0de_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/3637401835/e96f6955cd6bfd3d7bf78002ebdbd0de_normal.jpeg","profile_link_color":"2FC2EF","profile_sidebar_border_color":"181A1E","profile_sidebar_fill_color":"252429","profile_text_color":"666666","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":{"id":"2571b7720cd62ad3","url":"https://api.twitter.com/1.1/geo/id/2571b7720cd62ad3.json","place_type":"city","name":"Highlands Ranch","full_name":"Highlands Ranch, CO","country_code":"US","country":"United States","bounding_box":{"type":"Polygon","coordinates":[[[-105.05306,39.510546],[-104.899484,39.510546],[-104.899484,39.566782],[-105.05306,39.566782]]]},"attributes":{}},"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[{"text":"java","indices":[26,31]},{"text":"c","indices":[35,37]}],"symbols":[],"urls":[],"user_mentions":[]},"favorited":false,"retweeted":false,"lang":"en"},{"metadata":{"result_type":"recent","iso_language_code":"pt"},"created_at":"Fri Jul 26 19:17:48 +0000 2013","id":360841400004251648,"id_str":"360841400004251648","text":"RT @maribalbe: No Brasil? Piada! RT @rogerio_gentil: Desenvolvedores em #PHP e #Java ganham até R$ 9 mil http://t.co/Cj6dotO8Be // #souDev","source":"TweetDeck","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":188855964,"id_str":"188855964","name":"Rodrigo Baron","screen_name":"b4r0n__","location":"Medianeira","description":"Full-stack Rails Developer. Addicts script codes and Internet App Dev. Startup Co-Founder: http://t.co/Vvj2Un84yC","url":"http://t.co/WEJSgUafv6","entities":{"url":{"urls":[{"url":"http://t.co/WEJSgUafv6","expanded_url":"http://about.me/baron.rodrigo","display_url":"about.me/baron.rodrigo","indices":[0,22]}]},"description":{"urls":[{"url":"http://t.co/Vvj2Un84yC","expanded_url":"http://www.saifer.com.br","display_url":"saifer.com.br","indices":[91,113]}]}},"protected":false,"followers_count":121,"friends_count":183,"listed_count":1,"created_at":"Thu Sep 09 19:15:20 +0000 2010","favourites_count":105,"utc_offset":-10800,"time_zone":"Brasilia","geo_enabled":true,"verified":false,"statuses_count":3777,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"FFFFFF","profile_background_image_url":"http://a0.twimg.com/images/themes/theme14/bg.gif","profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme14/bg.gif","profile_background_tile":true,"profile_image_url":"http://a0.twimg.com/profile_images/378800000105833766/cfe4372a0fce180e09813ab02f909a4d_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/378800000105833766/cfe4372a0fce180e09813ab02f909a4d_normal.jpeg","profile_link_color":"006DCC","profile_sidebar_border_color":"FFFFFF","profile_sidebar_fill_color":"EFEFEF","profile_text_color":"333333","profile_use_background_image":false,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweeted_status":{"metadata":{"result_type":"recent","iso_language_code":"pt"},"created_at":"Fri Jul 26 14:37:46 +0000 2013","id":360770928357027840,"id_str":"360770928357027840","text":"No Brasil? Piada! RT @rogerio_gentil: Desenvolvedores em #PHP e #Java ganham até R$ 9 mil http://t.co/Cj6dotO8Be // #souDev","source":"TweetDeck","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":41602902,"id_str":"41602902","name":"Marília Balbé","screen_name":"maribalbe","location":"Florianópolis, Brazil","description":"Certified Scrum Master. MCTS Microsoft Project 2010, Managing Projects. MTAC. Gaúcha. Feeling. Liderança. Agilidade. Follow me!","url":"http://t.co/v7BRmRMPEn","entities":{"url":{"urls":[{"url":"http://t.co/v7BRmRMPEn","expanded_url":"http://mariliabalbe.com","display_url":"mariliabalbe.com","indices":[0,22]}]},"description":{"urls":[]}},"protected":false,"followers_count":2065,"friends_count":1464,"listed_count":81,"created_at":"Thu May 21 15:05:11 +0000 2009","favourites_count":51,"utc_offset":-10800,"time_zone":"Brasilia","geo_enabled":true,"verified":false,"statuses_count":26222,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"9AE4E8","profile_background_image_url":"http://a0.twimg.com/images/themes/theme16/bg.gif","profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme16/bg.gif","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/2914714002/d2b0d4771ad8c92841a8d6a37d24c920_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/2914714002/d2b0d4771ad8c92841a8d6a37d24c920_normal.jpeg","profile_link_color":"0084B4","profile_sidebar_border_color":"BDDCAD","profile_sidebar_fill_color":"DDFFCC","profile_text_color":"333333","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":21,"favorite_count":1,"entities":{"hashtags":[{"text":"PHP","indices":[57,61]},{"text":"Java","indices":[64,69]},{"text":"souDev","indices":[116,123]}],"symbols":[],"urls":[{"url":"http://t.co/Cj6dotO8Be","expanded_url":"http://lnkd.in/zsQhdg","display_url":"lnkd.in/zsQhdg","indices":[90,112]}],"user_mentions":[{"screen_name":"rogerio_gentil","name":"Rogerio J. Gentil","id":71697548,"id_str":"71697548","indices":[21,36]}]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"pt"},"retweet_count":21,"favorite_count":0,"entities":{"hashtags":[{"text":"PHP","indices":[72,76]},{"text":"Java","indices":[79,84]},{"text":"souDev","indices":[131,138]}],"symbols":[],"urls":[{"url":"http://t.co/Cj6dotO8Be","expanded_url":"http://lnkd.in/zsQhdg","display_url":"lnkd.in/zsQhdg","indices":[105,127]}],"user_mentions":[{"screen_name":"maribalbe","name":"Marília Balbé","id":41602902,"id_str":"41602902","indices":[3,13]},{"screen_name":"rogerio_gentil","name":"Rogerio J. Gentil","id":71697548,"id_str":"71697548","indices":[36,51]}]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"pt"},{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Jul 26 19:16:15 +0000 2013","id":360841008411447296,"id_str":"360841008411447296","text":"RT @danielleandy: Finding a way to have our morning coffee together! #bestneighbors #technology #java #screenshot… http://t.co/ZakQ5Ip16C","source":"Twitter for iPhone","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":169605040,"id_str":"169605040","name":"Bucko","screen_name":"jonathonbuckley","location":"Los Angeles","description":"","url":"http://t.co/Vsgepye3eK","entities":{"url":{"urls":[{"url":"http://t.co/Vsgepye3eK","expanded_url":"http://jonathonbuckley.com","display_url":"jonathonbuckley.com","indices":[0,22]}]},"description":{"urls":[]}},"protected":false,"followers_count":2059,"friends_count":116,"listed_count":86,"created_at":"Thu Jul 22 18:56:12 +0000 2010","favourites_count":5,"utc_offset":-25200,"time_zone":"Pacific Time (US & Canada)","geo_enabled":true,"verified":false,"statuses_count":436,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"1A1B1F","profile_background_image_url":"http://a0.twimg.com/images/themes/theme9/bg.gif","profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme9/bg.gif","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/1097180716/8X10_6255_no_pink_small_normal.jpg","profile_image_url_https":"https://si0.twimg.com/profile_images/1097180716/8X10_6255_no_pink_small_normal.jpg","profile_link_color":"2FC2EF","profile_sidebar_border_color":"181A1E","profile_sidebar_fill_color":"252429","profile_text_color":"666666","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweeted_status":{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Jul 26 16:44:25 +0000 2013","id":360802801032499200,"id_str":"360802801032499200","text":"Finding a way to have our morning coffee together! #bestneighbors #technology #java #screenshot… http://t.co/ZakQ5Ip16C","source":"Instagram","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":259458041,"id_str":"259458041","name":"Danielle Anderson","screen_name":"danielleandy","location":"Los Angeles ","description":"","url":"http://t.co/JiAcLT2Wfg","entities":{"url":{"urls":[{"url":"http://t.co/JiAcLT2Wfg","expanded_url":"http://www.danielleandy.com","display_url":"danielleandy.com","indices":[0,22]}]},"description":{"urls":[]}},"protected":false,"followers_count":209,"friends_count":127,"listed_count":3,"created_at":"Tue Mar 01 23:31:29 +0000 2011","favourites_count":0,"utc_offset":null,"time_zone":null,"geo_enabled":false,"verified":false,"statuses_count":1451,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"C0DEED","profile_background_image_url":"http://a0.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme1/bg.png","profile_background_tile":false,"profile_image_url":"http://a0.twimg.com/profile_images/378800000130553531/82c57daecf779a8d61f17a53a44d31d3_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/378800000130553531/82c57daecf779a8d61f17a53a44d31d3_normal.jpeg","profile_banner_url":"https://pbs.twimg.com/profile_banners/259458041/1373653784","profile_link_color":"0084B4","profile_sidebar_border_color":"C0DEED","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":true,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":1,"favorite_count":0,"entities":{"hashtags":[{"text":"bestneighbors","indices":[51,65]},{"text":"technology","indices":[66,77]},{"text":"java","indices":[79,84]},{"text":"screenshot","indices":[85,96]}],"symbols":[],"urls":[{"url":"http://t.co/ZakQ5Ip16C","expanded_url":"http://instagram.com/p/cPHow7KIcS/","display_url":"instagram.com/p/cPHow7KIcS/","indices":[98,120]}],"user_mentions":[]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"en"},"retweet_count":1,"favorite_count":0,"entities":{"hashtags":[{"text":"bestneighbors","indices":[69,83]},{"text":"technology","indices":[84,95]},{"text":"java","indices":[97,102]},{"text":"screenshot","indices":[103,114]}],"symbols":[],"urls":[{"url":"http://t.co/ZakQ5Ip16C","expanded_url":"http://instagram.com/p/cPHow7KIcS/","display_url":"instagram.com/p/cPHow7KIcS/","indices":[116,138]}],"user_mentions":[{"screen_name":"danielleandy","name":"Danielle Anderson","id":259458041,"id_str":"259458041","indices":[3,16]}]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"en"},{"metadata":{"result_type":"recent","iso_language_code":"in"},"created_at":"Fri Jul 26 19:11:01 +0000 2013","id":360839690812784641,"id_str":"360839690812784641","text":"Rip-off or investor's dream? You decide...http://t.co/yLzRPs1Qly #Bandar #Lampung #java #Sumatra #Banten #Riau #Padang //","source":"HootSuite","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":854562582,"id_str":"854562582","name":"SKS Media Londres","screen_name":"SKSMediaLondres","location":"","description":"Nous sommes aussi #teamfollowback #followback @sksfollowback","url":"http://t.co/g6dzOOabDQ","entities":{"url":{"urls":[{"url":"http://t.co/g6dzOOabDQ","expanded_url":"http://www.niche-advertising.co.uk","display_url":"niche-advertising.co.uk","indices":[0,22]}]},"description":{"urls":[]}},"protected":false,"followers_count":1972,"friends_count":1380,"listed_count":100,"created_at":"Sun Sep 30 11:39:09 +0000 2012","favourites_count":439,"utc_offset":7200,"time_zone":"Amsterdam","geo_enabled":false,"verified":false,"statuses_count":144790,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"C0DEED","profile_background_image_url":"http://a0.twimg.com/profile_background_images/673161806/9c3894fdf100336e2f53e97bffeace6c.jpeg","profile_background_image_url_https":"https://si0.twimg.com/profile_background_images/673161806/9c3894fdf100336e2f53e97bffeace6c.jpeg","profile_background_tile":true,"profile_image_url":"http://a0.twimg.com/profile_images/2665504502/86c0fc8adfb2d7f13e687f7dbfddf876_normal.jpeg","profile_image_url_https":"https://si0.twimg.com/profile_images/2665504502/86c0fc8adfb2d7f13e687f7dbfddf876_normal.jpeg","profile_link_color":"0084B4","profile_sidebar_border_color":"FFFFFF","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[{"text":"Bandar","indices":[65,72]},{"text":"Lampung","indices":[73,81]},{"text":"java","indices":[82,87]},{"text":"Sumatra","indices":[88,96]},{"text":"Banten","indices":[97,104]},{"text":"Riau","indices":[105,110]},{"text":"Padang","indices":[111,118]}],"symbols":[],"urls":[{"url":"http://t.co/yLzRPs1Qly","expanded_url":"http://tinyurl.com/dxxrao5","display_url":"tinyurl.com/dxxrao5","indices":[42,64]}],"user_mentions":[]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"in"}],"search_metadata":{"completed_in":0.033,"max_id":360844831439855616,"max_id_str":"360844831439855616","next_results":"?max_id=360839690812784640&q=%23java&include_entities=1","query":"%23java","refresh_url":"?since_id=360844831439855616&q=%23java&include_entities=1","count":15,"since_id":0,"since_id_str":"0"}} + diff --git a/tests/src/test/resources/wiki.json b/tests/src/test/resources/wiki.json new file mode 100644 index 00000000..17bb1932 --- /dev/null +++ b/tests/src/test/resources/wiki.json @@ -0,0 +1,21 @@ +{ + "firstName": "John", + "lastName": "Smith", + "age": 25, + "address": { + "streetAddress": "21 2nd Street", + "city": "New York", + "state": "NY", + "postalCode": "10021" + }, + "phoneNumber": [ + { + "type": "home", + "number": "212 555-1234" + }, + { + "type": "fax", + "number": "646 555-4567" + } + ] +}