You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@johnzon.apache.org by rm...@apache.org on 2014/06/13 15:45:15 UTC
[3/6] initial import
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-core/src/main/java/org/apache/fleece/core/JsonReaderListenerFactory.java
----------------------------------------------------------------------
diff --git a/fleece-core/src/main/java/org/apache/fleece/core/JsonReaderListenerFactory.java b/fleece-core/src/main/java/org/apache/fleece/core/JsonReaderListenerFactory.java
new file mode 100644
index 0000000..653145d
--- /dev/null
+++ b/fleece-core/src/main/java/org/apache/fleece/core/JsonReaderListenerFactory.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fleece.core;
+
+public interface JsonReaderListenerFactory {
+ JsonReaderListener subObject();
+
+ JsonReaderListener subArray();
+}
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-core/src/main/java/org/apache/fleece/core/JsonStreamParser.java
----------------------------------------------------------------------
diff --git a/fleece-core/src/main/java/org/apache/fleece/core/JsonStreamParser.java b/fleece-core/src/main/java/org/apache/fleece/core/JsonStreamParser.java
new file mode 100644
index 0000000..ffcb870
--- /dev/null
+++ b/fleece-core/src/main/java/org/apache/fleece/core/JsonStreamParser.java
@@ -0,0 +1,386 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fleece.core;
+
+import javax.json.JsonException;
+import javax.json.stream.JsonLocation;
+import javax.json.stream.JsonParser;
+import javax.json.stream.JsonParsingException;
+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 static org.apache.fleece.core.Strings.asEscapedChar;
+
+public class JsonStreamParser implements JsonChars, EscapedStringAwareJsonParser {
+ private static final BufferCache<char[]> BUFFER_CACHE = new BufferCache<char[]>(Integer.getInteger("org.apache.fleece.default-char-buffer", 8192) /*BufferedReader.defaultCharBufferSize*/) {
+ @Override
+ protected char[] newValue(final int defaultSize) {
+ return new char[defaultSize];
+ }
+ };
+
+ private final Reader reader;
+ private final int maxStringSize;
+
+ // lexer state
+ private final char[] loadedChars;
+ private int availableLength = -1; // to trigger loading at first read() call
+ private int currentBufferIdx;
+
+ // current state
+ private Event event = null;
+ private Event lastEvent = null;
+ private String currentValue = null;
+ private String escapedValue = null;
+ // location
+ private int line = 1;
+ private int column = 1;
+ private int offset = 0;
+
+ private final ValueBuilder valueBuilder = new ValueBuilder();
+
+ public JsonStreamParser(final Reader reader, final int maxStringLength) {
+ this.reader = reader;
+ this.loadedChars = BUFFER_CACHE.getCache();
+ this.maxStringSize = maxStringLength < 0 ? loadedChars.length : maxStringLength;
+ }
+
+ public JsonStreamParser(final InputStream stream, final int maxStringLength) {
+ this(new InputStreamReader(stream), maxStringLength);
+ }
+
+ public JsonStreamParser(final InputStream in, final Charset charset, final int maxStringLength) {
+ this(new InputStreamReader(in, charset), maxStringLength);
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (event != null) {
+ return loadedChars[currentBufferIdx] != EOF;
+ }
+
+ try {
+ do {
+ readUntilEvent();
+ if (loadedChars[currentBufferIdx] == QUOTE) {
+ valueBuilder.reset(0); // actually offset = 1 but reset increments idx
+ boolean escape = false;
+
+ while (nextChar() != EOF && loadedChars[currentBufferIdx] != QUOTE && currentBufferIdx < valueBuilder.maxEnd) {
+ if (loadedChars[currentBufferIdx] == ESCAPE_CHAR) {
+ read();
+ escape = true;
+ }
+ valueBuilder.next();
+ }
+ currentValue = valueBuilder.readValue();
+
+ if (escape) { // this induces an overhead but that's not that often normally
+ final StringBuilder builder = new StringBuilder(currentValue.length());
+ boolean escaped = false;
+ for (final char current : currentValue.toCharArray()) {
+ if (current == ESCAPE_CHAR) {
+ escaped = true;
+ continue;
+ }
+ if (!escape) {
+ builder.append(current);
+ } else {
+ builder.append(asEscapedChar(current));
+ }
+ }
+ escapedValue = currentValue;
+ currentValue = builder.toString();
+ } else {
+ escapedValue = null;
+ }
+
+ readUntilEvent(); // we need to check if next char is a ':' to know it is a key
+ if (loadedChars[currentBufferIdx] == KEY_SEPARATOR) {
+ event = Event.KEY_NAME;
+ } else {
+ if (loadedChars[currentBufferIdx] != COMMA && loadedChars[currentBufferIdx] != END_OBJECT_CHAR && loadedChars[currentBufferIdx] != END_ARRAY_CHAR) {
+ throw new JsonParsingException("expecting end of structure or comma but got " + loadedChars[currentBufferIdx], createLocation());
+ }
+ currentBufferIdx--; // we are alredy in place so to avoid offset when calling readUntilEvent() going back
+ event = Event.VALUE_STRING;
+ }
+ return true;
+ } else if (loadedChars[currentBufferIdx] == START_OBJECT_CHAR) {
+ event = Event.START_OBJECT;
+ return true;
+ } else if (loadedChars[currentBufferIdx] == END_OBJECT_CHAR) {
+ event = Event.END_OBJECT;
+ return true;
+ } else if (loadedChars[currentBufferIdx] == START_ARRAY_CHAR) {
+ event = Event.START_ARRAY;
+ return true;
+ } else if (loadedChars[currentBufferIdx] == END_ARRAY_CHAR) {
+ event = Event.END_ARRAY;
+ return true;
+ } else if (isNumber()) {
+ valueBuilder.reset(-1); // reset will increment to check overflow
+ while (nextChar() != EOF && isNumber() && currentBufferIdx < valueBuilder.maxEnd) {
+ valueBuilder.next();
+ }
+ currentValue = valueBuilder.readValue();
+ currentBufferIdx--; // we are alredy in place so to avoid offset when calling readUntilEvent() going back
+ event = Event.VALUE_NUMBER;
+ return true;
+ } else if (loadedChars[currentBufferIdx] == TRUE_T) {
+ if (read() != TRUE_R || read() != TRUE_U || read() != TRUE_E) {
+ throw new JsonParsingException("true expected", createLocation());
+ }
+ event = Event.VALUE_TRUE;
+ return true;
+ } else if (loadedChars[currentBufferIdx] == FALSE_F) {
+ if (read() != FALSE_A || read() != FALSE_L || read() != FALSE_S || read() != FALSE_E) {
+ throw new JsonParsingException("false expected", createLocation());
+ }
+ event = Event.VALUE_FALSE;
+ return true;
+ } else if (loadedChars[currentBufferIdx] == NULL_N) {
+ if (read() != NULL_U || read() != NULL_L || read() != NULL_L) {
+ throw new JsonParsingException("null expected", createLocation());
+ }
+ event = Event.VALUE_NULL;
+ return true;
+ } else if (loadedChars[currentBufferIdx] == EOF) {
+ return false;
+ } else if (loadedChars[currentBufferIdx] == COMMA) {
+ if (event != null && event != Event.KEY_NAME && event != Event.VALUE_STRING && event != Event.VALUE_NUMBER && event != Event.VALUE_TRUE && event != Event.VALUE_FALSE && event != Event.VALUE_NULL) {
+ throw new JsonParsingException("unexpected comma", createLocation());
+ }
+ } else {
+ throw new JsonParsingException("unexpected character: '" + loadedChars[currentBufferIdx] + "'", createLocation());
+ }
+ } while (true);
+ } catch (final IOException e) {
+ throw new JsonParsingException("unknown state", createLocation());
+ }
+ }
+
+ private StringBuilder savePreviousStringBeforeOverflow(int start, StringBuilder previousParts) {
+ final int length = currentBufferIdx - start;
+ previousParts = (previousParts == null ? new StringBuilder(length * 2) : previousParts).append(loadedChars, start, length);
+ return previousParts;
+ }
+
+ private boolean isNumber() {
+ return isNumber(loadedChars[currentBufferIdx]) || loadedChars[currentBufferIdx] == DOT || loadedChars[currentBufferIdx] == MINUS || loadedChars[currentBufferIdx] == PLUS || loadedChars[currentBufferIdx] == EXP_LOWERCASE || loadedChars[currentBufferIdx] == EXP_UPPERCASE;
+ }
+
+ private static boolean isNumber(final char value) {
+ return value >= ZERO && value <= NINE;
+ }
+
+ private void readUntilEvent() throws IOException {
+ read();
+ skipNotEventChars();
+ }
+
+ private void skipNotEventChars() throws IOException {
+ int read = 0;
+ do {
+ final int current = currentBufferIdx;
+ while (currentBufferIdx < availableLength) {
+ if (loadedChars[currentBufferIdx] > SPACE) {
+ final int diff = currentBufferIdx - current;
+ offset += diff;
+ column += diff;
+ return;
+ } else if (loadedChars[currentBufferIdx] == EOL) {
+ line++;
+ column = 0;
+ }
+ currentBufferIdx++;
+ }
+ read();
+ read++;
+ }
+ while (loadedChars[currentBufferIdx] != EOF && read < loadedChars.length); // don't accept more space than buffer size to avoid DoS
+ if (read == loadedChars.length) {
+ throw new JsonParsingException("Too much spaces (>" + loadedChars.length + ")", createLocation());
+ }
+ }
+
+ public JsonLocationImpl createLocation() {
+ return new JsonLocationImpl(line, column, offset);
+ }
+
+ private char read() throws IOException {
+ incr();
+ return nextChar();
+ }
+
+ private char nextChar() throws IOException {
+ if (overflowIfNeeded()) {
+ offset--;
+ column--;
+ return EOF;
+ }
+ return loadedChars[currentBufferIdx];
+ }
+
+ private int incr() {
+ offset++;
+ column++;
+ currentBufferIdx++;
+ return currentBufferIdx;
+ }
+
+ private boolean overflowIfNeeded() throws IOException {
+ if (currentBufferIdx >= availableLength) {
+ availableLength = reader.read(loadedChars, 0, loadedChars.length);
+ currentBufferIdx = 0;
+ if (availableLength <= 0) { // 0 or -1 typically
+ loadedChars[0] = EOF;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Event next() {
+ if (event == null) {
+ hasNext();
+ }
+ lastEvent = event;
+ event = null;
+ return lastEvent;
+ }
+
+ @Override
+ public String getString() {
+ if (lastEvent == Event.KEY_NAME || lastEvent == Event.VALUE_STRING || lastEvent == Event.VALUE_NUMBER) {
+ return currentValue;
+ }
+ throw new IllegalStateException(event + " doesnt support getString()");
+ }
+
+ @Override
+ public boolean isIntegralNumber() {
+ for (int i = 0; i < currentValue.length(); i++) {
+ if (!isNumber(currentValue.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int getInt() {
+ if (lastEvent != Event.VALUE_NUMBER) {
+ throw new IllegalStateException(event + " doesn't support getInt()");
+ }
+ return Integer.parseInt(currentValue);
+ }
+
+ @Override
+ public long getLong() {
+ if (lastEvent != Event.VALUE_NUMBER) {
+ throw new IllegalStateException(event + " doesn't support getLong()");
+ }
+ return Long.parseLong(currentValue);
+ }
+
+ @Override
+ public BigDecimal getBigDecimal() {
+ if (lastEvent != Event.VALUE_NUMBER) {
+ throw new IllegalStateException(event + " doesn't support getBigDecimal()");
+ }
+ return new BigDecimal(currentValue);
+ }
+
+ @Override
+ public JsonLocation getLocation() {
+ return createLocation();
+ }
+
+ @Override
+ public void close() {
+ BUFFER_CACHE.release(loadedChars);
+ try {
+ reader.close();
+ } catch (final IOException e) {
+ throw new JsonException(e.getMessage(), e);
+ }
+ }
+
+ public static JsonLocation location(final JsonParser parser) {
+ if (JsonStreamParser.class.isInstance(parser)) {
+ return JsonStreamParser.class.cast(parser).createLocation();
+ }
+ return new JsonLocationImpl(-1, -1, -1);
+ }
+
+ @Override
+ public String getEscapedString() {
+ return escapedValue;
+ }
+
+ private class ValueBuilder {
+ private int start;
+ private int maxEnd;
+ private StringBuilder previousParts = null;
+
+ public void next() {
+ if (incr() >= availableLength) { // overflow case
+ previousParts = savePreviousStringBeforeOverflow(start, previousParts);
+ start = 0;
+ maxEnd = maxStringSize;
+ }
+ }
+
+ public String readValue() {
+ if (loadedChars[currentBufferIdx] == EOF) {
+ throw new JsonParsingException("Can't read string", createLocation());
+ }
+
+ final int length = currentBufferIdx - start;
+ if (length >= maxStringSize) {
+ throw new JsonParsingException("String too long", createLocation());
+ }
+
+ final String currentValue = new String(loadedChars, start, length);
+ if (previousParts != null && previousParts.length() > 0) {
+ return previousParts.append(currentValue).toString();
+ }
+ return currentValue;
+ }
+
+ public void reset(final int offset) {
+ if (incr() < availableLength) { // direct overflow case
+ start = currentBufferIdx + offset;
+ maxEnd = start + maxStringSize;
+ } else {
+ maxEnd = maxStringSize - (maxEnd - start);
+ start = 0;
+ }
+ if (previousParts != null) {
+ previousParts.setLength(0);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-core/src/main/java/org/apache/fleece/core/JsonStringImpl.java
----------------------------------------------------------------------
diff --git a/fleece-core/src/main/java/org/apache/fleece/core/JsonStringImpl.java b/fleece-core/src/main/java/org/apache/fleece/core/JsonStringImpl.java
new file mode 100644
index 0000000..d0bdfa3
--- /dev/null
+++ b/fleece-core/src/main/java/org/apache/fleece/core/JsonStringImpl.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fleece.core;
+
+import javax.json.JsonString;
+import javax.json.JsonValue;
+
+public class JsonStringImpl implements JsonString {
+ private final String value;
+ private String escape;
+ private Integer hashCode = null;
+
+ public JsonStringImpl(final String value) {
+ this(value, null);
+ }
+
+ public JsonStringImpl(final String value, final String escaped) {
+ this.value = value;
+ this.escape = escaped;
+ }
+
+ @Override
+ public String getString() {
+ return value;
+ }
+
+ @Override
+ public CharSequence getChars() {
+ return value;
+ }
+
+ @Override
+ public ValueType getValueType() {
+ return ValueType.STRING;
+ }
+
+ @Override
+ public String toString() {
+ if (escape == null) {
+ escape = Strings.escape(value);
+ }
+ return escape;
+ }
+
+ @Override
+ public int hashCode() {
+ if (hashCode == null) {
+ hashCode = value.hashCode();
+ }
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ return JsonString.class.isInstance(obj) && JsonString.class.cast(obj).getString().equals(value);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-core/src/main/java/org/apache/fleece/core/JsonWriterFactoryImpl.java
----------------------------------------------------------------------
diff --git a/fleece-core/src/main/java/org/apache/fleece/core/JsonWriterFactoryImpl.java b/fleece-core/src/main/java/org/apache/fleece/core/JsonWriterFactoryImpl.java
new file mode 100644
index 0000000..4217cf4
--- /dev/null
+++ b/fleece-core/src/main/java/org/apache/fleece/core/JsonWriterFactoryImpl.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fleece.core;
+
+import javax.json.JsonWriter;
+import javax.json.JsonWriterFactory;
+import javax.json.stream.JsonGeneratorFactory;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.Map;
+
+public class JsonWriterFactoryImpl implements JsonWriterFactory {
+ private final Map<String, ?> config;
+ private final JsonGeneratorFactory factory;
+
+ public JsonWriterFactoryImpl(final Map<String, ?> config) {
+ this.config = config;
+ this.factory = new JsonGeneratorFactoryImpl(config);
+ }
+
+ @Override
+ public JsonWriter createWriter(final Writer writer) {
+ return new JsonWriterImpl(factory.createGenerator(writer));
+ }
+
+ @Override
+ public JsonWriter createWriter(final OutputStream out) {
+ return createWriter(new OutputStreamWriter(out));
+ }
+
+ @Override
+ public JsonWriter createWriter(final OutputStream out, final Charset charset) {
+ return createWriter(new OutputStreamWriter(out, charset));
+ }
+
+ @Override
+ public Map<String, ?> getConfigInUse() {
+ return config;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-core/src/main/java/org/apache/fleece/core/JsonWriterImpl.java
----------------------------------------------------------------------
diff --git a/fleece-core/src/main/java/org/apache/fleece/core/JsonWriterImpl.java b/fleece-core/src/main/java/org/apache/fleece/core/JsonWriterImpl.java
new file mode 100644
index 0000000..2c3c3a9
--- /dev/null
+++ b/fleece-core/src/main/java/org/apache/fleece/core/JsonWriterImpl.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fleece.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.JsonStructure;
+import javax.json.JsonWriter;
+import javax.json.stream.JsonGenerator;
+import java.io.Flushable;
+import java.io.IOException;
+
+public class JsonWriterImpl implements JsonWriter, Flushable {
+ private final JsonGenerator generator;
+
+ public JsonWriterImpl(final JsonGenerator generator) {
+ this.generator = generator;
+ }
+
+ @Override
+ public void writeArray(final JsonArray array) {
+ generator.write(array);
+ }
+
+ @Override
+ public void writeObject(final JsonObject object) {
+ generator.write(object);
+ }
+
+ @Override
+ public void write(final JsonStructure value) {
+ generator.write(value);
+ }
+
+ @Override
+ public void close() {
+ generator.close();
+ }
+
+ @Override
+ public void flush() throws IOException {
+ generator.flush();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-core/src/main/java/org/apache/fleece/core/Strings.java
----------------------------------------------------------------------
diff --git a/fleece-core/src/main/java/org/apache/fleece/core/Strings.java b/fleece-core/src/main/java/org/apache/fleece/core/Strings.java
new file mode 100644
index 0000000..bb13093
--- /dev/null
+++ b/fleece-core/src/main/java/org/apache/fleece/core/Strings.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fleece.core;
+
+public class Strings implements JsonChars {
+ private static final BufferCache<StringBuilder> BUFFER_CACHE = new BufferCache<StringBuilder>(Integer.getInteger("org.apache.fleece.default-string-builder", 1024)) {
+ @Override
+ protected StringBuilder newValue(final int defaultSize) {
+ return new StringBuilder(defaultSize);
+ }
+ };
+
+ private static final String UNICODE_PREFIX = "\\u";
+ private static final String UNICODE_PREFIX_HELPER = "000";
+
+ public static char asEscapedChar(final char current) {
+ switch (current) {
+ case 'r':
+ return '\r';
+ case 't':
+ return '\t';
+ case 'b':
+ return '\b';
+ case 'f':
+ return '\f';
+ case 'n':
+ return '\n';
+ case '"':
+ return '\"';
+ }
+ return current;
+ }
+
+ public static String escape(final String value) {
+ final StringBuilder builder = BUFFER_CACHE.getCache();
+ try {
+ for (int i = 0; i < value.length(); i++) {
+ final char c = value.charAt(i);
+ switch (c) {
+ case QUOTE:
+ case ESCAPE_CHAR:
+ builder.append(ESCAPE_CHAR).append(c);
+ break;
+ default:
+ if (c < SPACE) { // we could do a single switch but actually we should rarely enter this if so no need to pay it
+ switch (c) {
+ case EOL:
+ builder.append("\\n");
+ break;
+ case '\r':
+ builder.append("\\r");
+ break;
+ case '\t':
+ builder.append("\\t");
+ break;
+ case '\b':
+ builder.append("\\b");
+ break;
+ case '\f':
+ builder.append("\\f");
+ break;
+ default:
+ builder.append(toUnicode(c));
+ }
+ } else if ((c >= '\u0080' && c < '\u00a0') || (c >= '\u2000' && c < '\u2100')) {
+ builder.append(toUnicode(c));
+ } else {
+ builder.append(c);
+ }
+ }
+ }
+ final String s = builder.toString();
+ return s;
+ } finally {
+ builder.setLength(0);
+ BUFFER_CACHE.release(builder);
+ }
+ }
+
+ private static String toUnicode(final char c) {
+ final String hex = UNICODE_PREFIX_HELPER + Integer.toHexString(c);
+ return UNICODE_PREFIX + hex.substring(hex.length() - 4);
+ }
+
+ private Strings() {
+ // no-op
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-core/src/main/resources/META-INF/services/javax.json.spi.JsonProvider
----------------------------------------------------------------------
diff --git a/fleece-core/src/main/resources/META-INF/services/javax.json.spi.JsonProvider b/fleece-core/src/main/resources/META-INF/services/javax.json.spi.JsonProvider
new file mode 100644
index 0000000..0dbd3c3
--- /dev/null
+++ b/fleece-core/src/main/resources/META-INF/services/javax.json.spi.JsonProvider
@@ -0,0 +1 @@
+org.apache.fleece.core.JsonProviderImpl
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-core/src/test/java/org/apache/fleece/core/JsonArrayBuilderImplTest.java
----------------------------------------------------------------------
diff --git a/fleece-core/src/test/java/org/apache/fleece/core/JsonArrayBuilderImplTest.java b/fleece-core/src/test/java/org/apache/fleece/core/JsonArrayBuilderImplTest.java
new file mode 100644
index 0000000..42717c6
--- /dev/null
+++ b/fleece-core/src/test/java/org/apache/fleece/core/JsonArrayBuilderImplTest.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fleece.core;
+
+import org.junit.Test;
+
+import javax.json.Json;
+import javax.json.JsonArrayBuilder;
+
+import static org.junit.Assert.assertEquals;
+
+public class JsonArrayBuilderImplTest {
+ @Test
+ public void array() {
+ final JsonArrayBuilder builder = Json.createArrayBuilder();
+ builder.add("a").add("b");
+ assertEquals("[\"a\",\"b\"]", builder.build().toString());
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-core/src/test/java/org/apache/fleece/core/JsonArrayImplTest.java
----------------------------------------------------------------------
diff --git a/fleece-core/src/test/java/org/apache/fleece/core/JsonArrayImplTest.java b/fleece-core/src/test/java/org/apache/fleece/core/JsonArrayImplTest.java
new file mode 100644
index 0000000..0b89de4
--- /dev/null
+++ b/fleece-core/src/test/java/org/apache/fleece/core/JsonArrayImplTest.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fleece.core;
+
+import org.apache.fleece.core.JsonArrayImpl;
+import org.apache.fleece.core.JsonStringImpl;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class JsonArrayImplTest {
+ @Test
+ public void arrayToString() {
+ final JsonArrayImpl object = new JsonArrayImpl();
+ object.addInternal(new JsonStringImpl("a"));
+ object.addInternal(new JsonStringImpl("b"));
+ assertEquals("[\"a\",\"b\"]", object.toString());
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-core/src/test/java/org/apache/fleece/core/JsonGeneratorImplTest.java
----------------------------------------------------------------------
diff --git a/fleece-core/src/test/java/org/apache/fleece/core/JsonGeneratorImplTest.java b/fleece-core/src/test/java/org/apache/fleece/core/JsonGeneratorImplTest.java
new file mode 100644
index 0000000..6d9c4e5
--- /dev/null
+++ b/fleece-core/src/test/java/org/apache/fleece/core/JsonGeneratorImplTest.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fleece.core;
+
+import org.junit.Test;
+
+import javax.json.Json;
+import javax.json.stream.JsonGenerator;
+import java.io.ByteArrayOutputStream;
+import java.util.HashMap;
+
+import static org.junit.Assert.assertEquals;
+
+public class JsonGeneratorImplTest {
+ @Test
+ public void emptyArray() {
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ final JsonGenerator generator = Json.createGenerator(baos);
+ generator.writeStartArray().writeEnd().close();
+ assertEquals("[]", new String(baos.toByteArray()));
+ }
+
+ @Test
+ public void simpleArray() {
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ Json.createGenerator(baos).writeStartArray().write(1).write(2).writeEnd().close();
+ assertEquals("[1,2]", new String(baos.toByteArray()));
+ }
+
+ @Test
+ public void generate() {
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ final JsonGenerator generator = Json.createGenerator(baos);
+
+ 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().close();
+
+ assertEquals("{\"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\"}]}", new String(baos.toByteArray()));
+ }
+
+ @Test
+ public void pretty() {
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ final JsonGenerator generator = Json.createGeneratorFactory(new HashMap<String, Object>() {{
+ put(JsonGenerator.PRETTY_PRINTING, true);
+ }}).createGenerator(baos);
+
+ 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().close();
+
+ assertEquals("{\n" +
+ " \"firstName\":\"John\",\n" +
+ " \"lastName\":\"Smith\",\n" +
+ " \"age\":25,\n" +
+ " \"address\":{\n" +
+ " \"streetAddress\":\"21 2nd Street\",\n" +
+ " \"city\":\"New York\",\n" +
+ " \"state\":\"NY\",\n" +
+ " \"postalCode\":\"10021\"\n" +
+ " },\n" +
+ " \"phoneNumber\":[\n" +
+ " {\n" +
+ " \"type\":\"home\",\n" +
+ " \"number\":\"212 555-1234\"\n" +
+ " },\n" +
+ " {\n" +
+ " \"type\":\"fax\",\n" +
+ " \"number\":\"646 555-4567\"\n" +
+ " }\n" +
+ " ]\n" +
+ "}", new String(baos.toByteArray()));
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-core/src/test/java/org/apache/fleece/core/JsonObjectBuilderImplTest.java
----------------------------------------------------------------------
diff --git a/fleece-core/src/test/java/org/apache/fleece/core/JsonObjectBuilderImplTest.java b/fleece-core/src/test/java/org/apache/fleece/core/JsonObjectBuilderImplTest.java
new file mode 100644
index 0000000..ea14a71
--- /dev/null
+++ b/fleece-core/src/test/java/org/apache/fleece/core/JsonObjectBuilderImplTest.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fleece.core;
+
+import org.junit.Test;
+
+import javax.json.Json;
+import javax.json.JsonObjectBuilder;
+
+import static org.junit.Assert.assertEquals;
+
+public class JsonObjectBuilderImplTest {
+ @Test
+ public void build() {
+ final JsonObjectBuilder builder = Json.createObjectBuilder();
+ builder.add("a", "b");
+ assertEquals("{\"a\":\"b\"}", builder.build().toString());
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-core/src/test/java/org/apache/fleece/core/JsonObjectImplTest.java
----------------------------------------------------------------------
diff --git a/fleece-core/src/test/java/org/apache/fleece/core/JsonObjectImplTest.java b/fleece-core/src/test/java/org/apache/fleece/core/JsonObjectImplTest.java
new file mode 100644
index 0000000..a7aff07
--- /dev/null
+++ b/fleece-core/src/test/java/org/apache/fleece/core/JsonObjectImplTest.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fleece.core;
+
+import org.apache.fleece.core.JsonObjectImpl;
+import org.apache.fleece.core.JsonStringImpl;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class JsonObjectImplTest {
+ @Test
+ public void objectToString() {
+ final JsonObjectImpl object = new JsonObjectImpl();
+ object.putInternal("a", new JsonStringImpl("b"));
+ assertEquals("{\"a\":\"b\"}", object.toString());
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-core/src/test/java/org/apache/fleece/core/JsonParserTest.java
----------------------------------------------------------------------
diff --git a/fleece-core/src/test/java/org/apache/fleece/core/JsonParserTest.java b/fleece-core/src/test/java/org/apache/fleece/core/JsonParserTest.java
new file mode 100644
index 0000000..6e4727f
--- /dev/null
+++ b/fleece-core/src/test/java/org/apache/fleece/core/JsonParserTest.java
@@ -0,0 +1,442 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fleece.core;
+
+import org.apache.fleece.core.JsonArrayImpl;
+import org.apache.fleece.core.JsonNumberImpl;
+import org.apache.fleece.core.JsonObjectImpl;
+import org.apache.fleece.core.JsonParserFactoryImpl;
+import org.apache.fleece.core.JsonReaderImpl;
+import org.apache.fleece.core.JsonStreamParser;
+import org.apache.fleece.core.JsonStringImpl;
+import org.junit.Test;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonReader;
+import javax.json.stream.JsonParser;
+import javax.json.stream.JsonParsingException;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.util.Collections;
+import java.util.HashMap;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class JsonParserTest {
+ private void assertSimple(final JsonParser parser) {
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.START_OBJECT, event);
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.KEY_NAME, event);
+ assertEquals("a", parser.getString());
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.VALUE_STRING, event);
+ assertEquals("b", parser.getString());
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.KEY_NAME, event);
+ assertEquals("c", parser.getString());
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.VALUE_NUMBER, event);
+ assertTrue(parser.isIntegralNumber());
+ assertEquals(4, parser.getInt());
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.KEY_NAME, event);
+ assertEquals("d", parser.getString());
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.START_ARRAY, event);
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.VALUE_NUMBER, event);
+ assertEquals(1, parser.getInt());
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.VALUE_NUMBER, event);
+ assertEquals(2, parser.getInt());
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.END_ARRAY, event);
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.END_OBJECT, event);
+ }
+ {
+ assertFalse(parser.hasNext());
+ }
+ parser.close();
+ }
+
+ @Test
+ public void array() {
+ final JsonReader loadInMemReader = Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/array.json"));
+ assertNotNull(loadInMemReader);
+ final JsonArray array = loadInMemReader.readArray();
+ assertNotNull(array);
+
+ final JsonParser parser = Json.createParserFactory(Collections.<String, Object>emptyMap()).createParser(array);
+ assertNotNull(parser);
+
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.START_ARRAY, event);
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.VALUE_STRING, event);
+ assertEquals("a", parser.getString());
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.VALUE_NUMBER, event);
+ assertEquals(1, parser.getInt());
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.START_OBJECT, event);
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.KEY_NAME, event);
+ assertEquals("b", parser.getString());
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.VALUE_STRING, event);
+ assertEquals("c", parser.getString());
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.END_OBJECT, event);
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.VALUE_NUMBER, event);
+ assertEquals(5, parser.getInt());
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.END_ARRAY, event);
+ }
+ {
+ assertFalse(parser.hasNext());
+ }
+ }
+
+ @Test
+ public void simpleInMemory() {
+ final JsonObjectImpl simple = new JsonObjectImpl();
+ simple.putInternal("a", new JsonStringImpl("b"));
+ simple.putInternal("c", new JsonNumberImpl(new BigDecimal(4)));
+ final JsonArrayImpl array = new JsonArrayImpl();
+ array.addInternal(new JsonNumberImpl(new BigDecimal(1)));
+ array.addInternal(new JsonNumberImpl(new BigDecimal(2)));
+ simple.putInternal("d", array);
+
+ final JsonParser parser = Json.createParserFactory(Collections.<String, Object>emptyMap()).createParser(simple);
+ assertNotNull(parser);
+ assertSimple(parser);
+ }
+
+ @Test
+ public void simple() {
+ final JsonParser parser = Json.createParser(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/simple.json"));
+ assertNotNull(parser);
+ assertSimple(parser);
+ }
+
+ @Test
+ public void nested() {
+ final JsonParser parser = Json.createParser(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/nested.json"));
+ assertNotNull(parser);
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.START_OBJECT, event);
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.KEY_NAME, event);
+ assertEquals("a", parser.getString());
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.VALUE_STRING, event);
+ assertEquals("b", parser.getString());
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.KEY_NAME, event);
+ assertEquals("c", parser.getString());
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.START_OBJECT, event);
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.KEY_NAME, event);
+ assertEquals("d", parser.getString());
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.START_ARRAY, event);
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.VALUE_NUMBER, event);
+ assertEquals(1, parser.getInt());
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.VALUE_NUMBER, event);
+ assertEquals(2, parser.getInt());
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.END_ARRAY, event);
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.END_OBJECT, event);
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.END_OBJECT, event);
+ }
+ {
+ assertFalse(parser.hasNext());
+ }
+ parser.close();
+ }
+
+ @Test
+ public void bigdecimal() {
+ final JsonParser parser = Json.createParser(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/bigdecimal.json"));
+ assertNotNull(parser);
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.START_OBJECT, event);
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.KEY_NAME, event);
+ assertEquals("a", parser.getString());
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.VALUE_NUMBER, event);
+ assertFalse(parser.isIntegralNumber());
+ assertEquals(new BigDecimal("1.23E3"), parser.getBigDecimal());
+ }
+ {
+ assertTrue(parser.hasNext());
+ final JsonParser.Event event = parser.next();
+ assertNotNull(event);
+ assertEquals(JsonParser.Event.END_OBJECT, event);
+ }
+ {
+ assertFalse(parser.hasNext());
+ }
+ parser.close();
+ }
+
+ @Test
+ public void escaping() {
+ final JsonParser parser = Json.createParser(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/escaping.json"));
+ parser.next();
+ parser.next();
+ assertEquals("\"", parser.getString());
+ parser.next();
+ assertEquals("\t", parser.getString());
+ parser.next();
+ assertFalse(parser.hasNext());
+ parser.close();
+ }
+
+ @Test
+ public void dosProtected() {
+ // strings
+ {
+ final JsonParser parser = Json.createParserFactory(new HashMap<String, Object>() {{
+ put(JsonParserFactoryImpl.MAX_STRING_LENGTH, 10);
+ }}).createParser(new InputStream() {
+ private int index = 0;
+
+ @Override
+ public int read() throws IOException {
+ switch (index) {
+ case 0:
+ index++;
+ return '{';
+ case 1:
+ index++;
+ return '"';
+ }
+ return 'a'; // infinite key
+ }
+ });
+ assertEquals(JsonParser.Event.START_OBJECT, parser.next());
+ try {
+ parser.next(); // should fail cause we try to make a OOME
+ fail();
+ } catch (final JsonParsingException expected) {
+ // no-op
+ }
+ parser.close();
+ }
+
+ // spaces
+ {
+ final JsonParser parser = Json.createParserFactory(new HashMap<String, Object>() {{
+ put(JsonParserFactoryImpl.MAX_STRING_LENGTH, 10);
+ }}).createParser(new InputStream() {
+ private int index = 0;
+
+ @Override
+ public int read() throws IOException {
+ switch (index) {
+ case 0:
+ index++;
+ return '{';
+ }
+ return ' '; // infinite spaces
+ }
+ });
+ assertEquals(JsonParser.Event.START_OBJECT, parser.next());
+ try { // should fail cause we try to make a OOME
+ while (parser.hasNext()) {
+ parser.next();
+ }
+ fail();
+ } catch (final JsonParsingException expected) {
+ // no-op
+ }
+ parser.close();
+ }
+ }
+
+ @Test
+ public void hasNext() {
+ final JsonParser parser = new JsonStreamParser(new ByteArrayInputStream("{}".getBytes()), 1000);
+ assertTrue(parser.hasNext());
+ assertTrue(parser.hasNext());
+ assertTrue(parser.hasNext());
+ assertTrue(parser.hasNext());
+ assertEquals(JsonParser.Event.START_OBJECT, parser.next());
+ parser.close();
+ }
+
+ @Test(expected = JsonParsingException.class)
+ public void commaChecks() {
+ // using a reader as wrapper of parser
+ new JsonReaderImpl(new ByteArrayInputStream("{\"z\":\"b\"\"j\":\"d\"}".getBytes())).read();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-core/src/test/java/org/apache/fleece/core/JsonReaderImplTest.java
----------------------------------------------------------------------
diff --git a/fleece-core/src/test/java/org/apache/fleece/core/JsonReaderImplTest.java b/fleece-core/src/test/java/org/apache/fleece/core/JsonReaderImplTest.java
new file mode 100644
index 0000000..6520095
--- /dev/null
+++ b/fleece-core/src/test/java/org/apache/fleece/core/JsonReaderImplTest.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fleece.core;
+
+import org.junit.Test;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.JsonReader;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
+public class JsonReaderImplTest {
+ @Test
+ public void simple() {
+ final JsonReader reader = Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/simple.json"));
+ assertNotNull(reader);
+ final JsonObject object = reader.readObject();
+ assertNotNull(object);
+ assertEquals(3, object.size());
+ assertEquals("b", object.getString("a"));
+ assertEquals(4, object.getInt("c"));
+ assertThat(object.get("d"), instanceOf(JsonArray.class));
+ final JsonArray array = object.getJsonArray("d");
+ assertNotNull(array);
+ assertEquals(2, array.size());
+ assertEquals(1, array.getInt(0));
+ assertEquals(2, array.getInt(1));
+ reader.close();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-core/src/test/java/org/apache/fleece/core/JsonWriterImplTest.java
----------------------------------------------------------------------
diff --git a/fleece-core/src/test/java/org/apache/fleece/core/JsonWriterImplTest.java b/fleece-core/src/test/java/org/apache/fleece/core/JsonWriterImplTest.java
new file mode 100644
index 0000000..8c3d921
--- /dev/null
+++ b/fleece-core/src/test/java/org/apache/fleece/core/JsonWriterImplTest.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fleece.core;
+
+import org.apache.fleece.core.JsonObjectImpl;
+import org.apache.fleece.core.JsonStringImpl;
+import org.junit.Test;
+
+import javax.json.Json;
+import javax.json.JsonWriter;
+import java.io.ByteArrayOutputStream;
+
+import static org.junit.Assert.assertEquals;
+
+public class JsonWriterImplTest {
+ @Test
+ public void writer() {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ final JsonWriter writer = Json.createWriter(out);
+ final JsonObjectImpl value = new JsonObjectImpl();
+ value.putInternal("a", new JsonStringImpl("b"));
+ writer.write(value);
+ writer.close();
+ assertEquals("{\"a\":\"b\"}", new String(out.toByteArray()));
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-core/src/test/resources/json/array.json
----------------------------------------------------------------------
diff --git a/fleece-core/src/test/resources/json/array.json b/fleece-core/src/test/resources/json/array.json
new file mode 100644
index 0000000..6e6bf04
--- /dev/null
+++ b/fleece-core/src/test/resources/json/array.json
@@ -0,0 +1,8 @@
+[
+ "a",
+ 1,
+ {
+ "b": "c"
+ },
+ 5
+]
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-core/src/test/resources/json/bigdecimal.json
----------------------------------------------------------------------
diff --git a/fleece-core/src/test/resources/json/bigdecimal.json b/fleece-core/src/test/resources/json/bigdecimal.json
new file mode 100644
index 0000000..7bbb4ea
--- /dev/null
+++ b/fleece-core/src/test/resources/json/bigdecimal.json
@@ -0,0 +1,3 @@
+{
+ "a": 1.23E3
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-core/src/test/resources/json/escaping.json
----------------------------------------------------------------------
diff --git a/fleece-core/src/test/resources/json/escaping.json b/fleece-core/src/test/resources/json/escaping.json
new file mode 100644
index 0000000..87965a1
--- /dev/null
+++ b/fleece-core/src/test/resources/json/escaping.json
@@ -0,0 +1,4 @@
+[
+ "\"",
+ "\\t"
+]
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-core/src/test/resources/json/nested.json
----------------------------------------------------------------------
diff --git a/fleece-core/src/test/resources/json/nested.json b/fleece-core/src/test/resources/json/nested.json
new file mode 100644
index 0000000..d6f09f8
--- /dev/null
+++ b/fleece-core/src/test/resources/json/nested.json
@@ -0,0 +1,9 @@
+{
+ "a": "b",
+ "c": {
+ "d": [
+ 1,
+ 2
+ ]
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-core/src/test/resources/json/simple.json
----------------------------------------------------------------------
diff --git a/fleece-core/src/test/resources/json/simple.json b/fleece-core/src/test/resources/json/simple.json
new file mode 100644
index 0000000..07dced1
--- /dev/null
+++ b/fleece-core/src/test/resources/json/simple.json
@@ -0,0 +1,8 @@
+{
+ "a": "b",
+ "c": 4,
+ "d": [
+ 1,
+ 2
+ ]
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-jaxrs/pom.xml
----------------------------------------------------------------------
diff --git a/fleece-jaxrs/pom.xml b/fleece-jaxrs/pom.xml
new file mode 100644
index 0000000..a211968
--- /dev/null
+++ b/fleece-jaxrs/pom.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>fleece</artifactId>
+ <groupId>org.apache.fleece</groupId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>fleece-jaxrs</artifactId>
+ <name>Fleece :: JAX-RS</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>javax.ws.rs</groupId>
+ <artifactId>javax.ws.rs-api</artifactId>
+ <version>2.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.fleece</groupId>
+ <artifactId>fleece-mapper</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-rs-client</artifactId>
+ <version>${cxf.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-frontend-jaxrs</artifactId>
+ <version>${cxf.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-transports-local</artifactId>
+ <version>${cxf.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <properties>
+ <cxf.version>3.0.0</cxf.version>
+ </properties>
+</project>
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-jaxrs/src/main/java/org/apache/fleece/jaxrs/DelegateProvider.java
----------------------------------------------------------------------
diff --git a/fleece-jaxrs/src/main/java/org/apache/fleece/jaxrs/DelegateProvider.java b/fleece-jaxrs/src/main/java/org/apache/fleece/jaxrs/DelegateProvider.java
new file mode 100644
index 0000000..12bcc80
--- /dev/null
+++ b/fleece-jaxrs/src/main/java/org/apache/fleece/jaxrs/DelegateProvider.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fleece.jaxrs;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+public abstract class DelegateProvider<T> implements MessageBodyWriter<T>, MessageBodyReader<T> {
+ private final MessageBodyReader<T> reader;
+ private final MessageBodyWriter<T> writer;
+
+ protected DelegateProvider(final MessageBodyReader<T> reader, final MessageBodyWriter<T> writer) {
+ this.reader = reader;
+ this.writer = writer;
+ }
+
+ @Override
+ public boolean isReadable(final Class<?> rawType, final Type genericType,
+ final Annotation[] annotations, final MediaType mediaType) {
+ return reader.isReadable(rawType, genericType, annotations, mediaType);
+ }
+
+ @Override
+ public T readFrom(final Class<T> rawType, final Type genericType,
+ final Annotation[] annotations, final MediaType mediaType,
+ final MultivaluedMap<String, String> httpHeaders,
+ final InputStream entityStream) throws IOException {
+ return reader.readFrom(rawType, genericType, annotations, mediaType, httpHeaders, entityStream);
+ }
+
+ @Override
+ public long getSize(final T t, final Class<?> rawType, final Type genericType,
+ final Annotation[] annotations, final MediaType mediaType) {
+ return writer.getSize(t, rawType, genericType, annotations, mediaType);
+ }
+
+ @Override
+ public boolean isWriteable(final Class<?> rawType, final Type genericType,
+ final Annotation[] annotations, final MediaType mediaType) {
+ return writer.isWriteable(rawType, genericType, annotations, mediaType);
+ }
+
+ @Override
+ public void writeTo(final T t, final Class<?> rawType, final Type genericType,
+ final Annotation[] annotations, final MediaType mediaType,
+ final MultivaluedMap<String, Object> httpHeaders,
+ final OutputStream entityStream) throws IOException {
+ writer.writeTo(t, rawType, genericType, annotations, mediaType, httpHeaders, entityStream);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-jaxrs/src/main/java/org/apache/fleece/jaxrs/FleeceMessageBodyReader.java
----------------------------------------------------------------------
diff --git a/fleece-jaxrs/src/main/java/org/apache/fleece/jaxrs/FleeceMessageBodyReader.java b/fleece-jaxrs/src/main/java/org/apache/fleece/jaxrs/FleeceMessageBodyReader.java
new file mode 100644
index 0000000..4a06e25
--- /dev/null
+++ b/fleece-jaxrs/src/main/java/org/apache/fleece/jaxrs/FleeceMessageBodyReader.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fleece.jaxrs;
+
+import org.apache.fleece.mapper.Mapper;
+import org.apache.fleece.mapper.MapperBuilder;
+
+import javax.json.JsonStructure;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.Provider;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Collection;
+
+import static javax.ws.rs.core.MediaType.WILDCARD;
+import static org.apache.fleece.jaxrs.Jsons.isJson;
+
+@Provider
+@Consumes(WILDCARD)
+public class FleeceMessageBodyReader<T> implements MessageBodyReader<T> {
+ private final Mapper mapper;
+
+ public FleeceMessageBodyReader() {
+ this(new MapperBuilder().setDoCloseOnStreams(false).build());
+ }
+
+ public FleeceMessageBodyReader(final Mapper mapper) {
+ this.mapper = mapper;
+ }
+
+ @Override
+ public boolean isReadable(final Class<?> rawType, final Type genericType,
+ final Annotation[] annotations, final MediaType mediaType) {
+ return isJson(mediaType)
+ && InputStream.class != rawType && Reader.class != rawType
+ && String.class != rawType
+ && !JsonStructure.class.isAssignableFrom(rawType);
+ }
+
+ @Override
+ public T readFrom(final Class<T> rawType, final Type genericType,
+ final Annotation[] annotations, final MediaType mediaType,
+ final MultivaluedMap<String, String> httpHeaders,
+ final InputStream entityStream) throws IOException {
+ if (rawType.isArray()) {
+ return (T) mapper.readArray(entityStream, rawType.getComponentType());
+ } else if (Collection.class.isAssignableFrom(rawType) && ParameterizedType.class.isInstance(genericType)) {
+ return (T) mapper.readCollection(entityStream, ParameterizedType.class.cast(genericType), rawType);
+ }
+ return mapper.readObject(entityStream, genericType);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-jaxrs/src/main/java/org/apache/fleece/jaxrs/FleeceMessageBodyWriter.java
----------------------------------------------------------------------
diff --git a/fleece-jaxrs/src/main/java/org/apache/fleece/jaxrs/FleeceMessageBodyWriter.java b/fleece-jaxrs/src/main/java/org/apache/fleece/jaxrs/FleeceMessageBodyWriter.java
new file mode 100644
index 0000000..b893222
--- /dev/null
+++ b/fleece-jaxrs/src/main/java/org/apache/fleece/jaxrs/FleeceMessageBodyWriter.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fleece.jaxrs;
+
+import org.apache.fleece.mapper.Mapper;
+import org.apache.fleece.mapper.MapperBuilder;
+
+import javax.json.JsonStructure;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.StreamingOutput;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Collection;
+
+import static javax.ws.rs.core.MediaType.WILDCARD;
+
+@Provider
+@Produces(WILDCARD)
+public class FleeceMessageBodyWriter<T> implements MessageBodyWriter<T> {
+ private final Mapper mapper;
+
+ public FleeceMessageBodyWriter() {
+ this(new MapperBuilder().setDoCloseOnStreams(false).build());
+ }
+
+ public FleeceMessageBodyWriter(final Mapper mapper) {
+ this.mapper = mapper;
+ }
+
+ @Override
+ public long getSize(final T t, final Class<?> rawType, final Type genericType,
+ final Annotation[] annotations, final MediaType mediaType) {
+ return -1;
+ }
+
+ @Override
+ public boolean isWriteable(final Class<?> rawType, final Type genericType,
+ final Annotation[] annotations, final MediaType mediaType) {
+ return Jsons.isJson(mediaType)
+ && InputStream.class != rawType
+ && OutputStream.class != rawType
+ && Writer.class != rawType
+ && StreamingOutput.class != rawType
+ && String.class != rawType
+ && Response.class != rawType
+ && !JsonStructure.class.isAssignableFrom(rawType);
+ }
+
+ @Override
+ public void writeTo(final T t, final Class<?> rawType, final Type genericType,
+ final Annotation[] annotations, final MediaType mediaType,
+ final MultivaluedMap<String, Object> httpHeaders,
+ final OutputStream entityStream) throws IOException {
+ if (rawType.isArray()) {
+ mapper.writeArray(t, entityStream);
+ } else if (Collection.class.isAssignableFrom(rawType) && ParameterizedType.class.isInstance(genericType)) {
+ mapper.writeArray(Collection.class.cast(t), entityStream);
+ } else {
+ mapper.writeObject(t, entityStream);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-jaxrs/src/main/java/org/apache/fleece/jaxrs/FleeceProvider.java
----------------------------------------------------------------------
diff --git a/fleece-jaxrs/src/main/java/org/apache/fleece/jaxrs/FleeceProvider.java b/fleece-jaxrs/src/main/java/org/apache/fleece/jaxrs/FleeceProvider.java
new file mode 100644
index 0000000..6f1beba
--- /dev/null
+++ b/fleece-jaxrs/src/main/java/org/apache/fleece/jaxrs/FleeceProvider.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fleece.jaxrs;
+
+import org.apache.fleece.mapper.Mapper;
+import org.apache.fleece.mapper.MapperBuilder;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.ext.Provider;
+
+import static javax.ws.rs.core.MediaType.WILDCARD;
+
+@Provider
+@Produces(WILDCARD)
+@Consumes(WILDCARD)
+public class FleeceProvider<T> extends DelegateProvider<T> {
+ public FleeceProvider(final Mapper mapper) {
+ super(new FleeceMessageBodyReader<T>(mapper), new FleeceMessageBodyWriter<T>(mapper));
+ }
+
+ public FleeceProvider() {
+ this(new MapperBuilder().setDoCloseOnStreams(false).build());
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-jaxrs/src/main/java/org/apache/fleece/jaxrs/Jsons.java
----------------------------------------------------------------------
diff --git a/fleece-jaxrs/src/main/java/org/apache/fleece/jaxrs/Jsons.java b/fleece-jaxrs/src/main/java/org/apache/fleece/jaxrs/Jsons.java
new file mode 100644
index 0000000..7872d28
--- /dev/null
+++ b/fleece-jaxrs/src/main/java/org/apache/fleece/jaxrs/Jsons.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fleece.jaxrs;
+
+import javax.ws.rs.core.MediaType;
+
+public class Jsons {
+ public static boolean isJson(final MediaType mediaType) {
+ if (mediaType != null) {
+ final String subtype = mediaType.getSubtype();
+ return "json".equalsIgnoreCase(subtype)
+ || "javascript".equals(subtype)
+ || "x-json".equals(subtype)
+ || "x-javascript".equals(subtype)
+ || subtype.endsWith("+json");
+ }
+ return true;
+ }
+
+ private Jsons() {
+ // no-op
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-fleece/blob/422e96f3/fleece-jaxrs/src/main/java/org/apache/fleece/jaxrs/JsrMessageBodyReader.java
----------------------------------------------------------------------
diff --git a/fleece-jaxrs/src/main/java/org/apache/fleece/jaxrs/JsrMessageBodyReader.java b/fleece-jaxrs/src/main/java/org/apache/fleece/jaxrs/JsrMessageBodyReader.java
new file mode 100644
index 0000000..f9a9e1c
--- /dev/null
+++ b/fleece-jaxrs/src/main/java/org/apache/fleece/jaxrs/JsrMessageBodyReader.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fleece.jaxrs;
+
+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;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Collections;
+
+import static javax.ws.rs.core.MediaType.WILDCARD;
+
+@Provider
+@Consumes(WILDCARD)
+public class JsrMessageBodyReader implements MessageBodyReader<JsonStructure> {
+ private final JsonReaderFactory factory;
+ private final boolean closeStream;
+
+ public JsrMessageBodyReader() {
+ this(Json.createReaderFactory(Collections.<String, Object>emptyMap()), false);
+ }
+
+ public JsrMessageBodyReader(final JsonReaderFactory factory, final boolean closeStream) {
+ this.factory = factory;
+ this.closeStream = closeStream;
+ }
+
+ @Override
+ public boolean isReadable(final Class<?> aClass, final Type type,
+ final Annotation[] annotations, final MediaType mediaType) {
+ return JsonStructure.class.isAssignableFrom(aClass);
+ }
+
+ @Override
+ public JsonStructure readFrom(final Class<JsonStructure> jsonStructureClass, final Type type,
+ final Annotation[] annotations, final MediaType mediaType,
+ final MultivaluedMap<String, String> stringStringMultivaluedMap,
+ final InputStream inputStream) throws IOException, WebApplicationException {
+ JsonReader reader = null;
+ try {
+ reader = factory.createReader(inputStream);
+ return reader.read();
+ } finally {
+ if (closeStream && reader != null) {
+ reader.close();
+ }
+ }
+ }
+}