Index: oak-core/src/main/java/org/apache/jackrabbit/oak/util/JsonBuilder.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/util/JsonBuilder.java (revision 1349715) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/util/JsonBuilder.java (revision 1349715) @@ -1,445 +0,0 @@ -/* - * 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.jackrabbit.oak.util; - -import java.io.IOException; - -/** - * Partially based on json-simple - * Limitation: arrays can only have primitive members (i.e. no arrays nor objects) - */ -public final class JsonBuilder { - final Appendable writer; - - private JsonBuilder(Appendable writer) { - this.writer = writer; - } - - public static JsonObjectBuilder create(Appendable writer) throws IOException { - return new JsonBuilder(writer).new JsonObjectBuilder(null); - } - - public final class JsonObjectBuilder { - private final JsonObjectBuilder parent; - - private boolean hasKeys; - - public JsonObjectBuilder(JsonObjectBuilder parent) throws IOException { - this.parent = parent; - writer.append('{'); - } - - public JsonObjectBuilder value(String key, String value) throws IOException { - write(key, encode(value)); - return this; - } - - public JsonObjectBuilder valueEncoded(String key, String value) throws IOException { - write(key, value); - return this; - } - - public JsonObjectBuilder value(String key, int value) throws IOException { - write(key, encode(value)); - return this; - } - - public JsonObjectBuilder value(String key, long value) throws IOException { - write(key, encode(value)); - return this; - } - - public JsonObjectBuilder value(String key, float value) throws IOException { - write(key, encode(value)); - return this; - } - - public JsonObjectBuilder value(String key, double value) throws IOException { - write(key, encode(value)); - return this; - } - - public JsonObjectBuilder value(String key, Number value) throws IOException { - write(key, encode(value)); - return this; - } - - public JsonObjectBuilder value(String key, boolean value) throws IOException { - write(key, encode(value)); - return this; - } - - public JsonObjectBuilder nil(String key) throws IOException { - write(key, "null"); - return this; - } - - public JsonObjectBuilder array(String key, String[] value) throws IOException { - write(key, encode(value)); - return this; - } - - public JsonObjectBuilder array(String key, int[] value) throws IOException { - write(key, encode(value)); - return this; - } - - public JsonObjectBuilder array(String key, long[] value) throws IOException { - write(key, encode(value)); - return this; - } - - public JsonObjectBuilder array(String key, float[] value) throws IOException { - write(key, encode(value)); - return this; - } - - public JsonObjectBuilder array(String key, double[] value) throws IOException { - write(key, encode(value)); - return this; - } - - public JsonObjectBuilder array(String key, Number[] value) throws IOException { - write(key, encode(value)); - return this; - } - - public JsonObjectBuilder array(String key, boolean[] value) throws IOException { - write(key, encode(value)); - return this; - } - - public JsonArrayBuilder array(String key) throws IOException { - writeKey(key); - return new JsonArrayBuilder(this); - } - - public JsonObjectBuilder object(String key) throws IOException { - writeKey(key); - return new JsonObjectBuilder(this); - } - - public JsonObjectBuilder build() throws IOException { - writer.append('}'); - return parent; - } - - //------------------------------------------< private >--- - - private void optionalComma() throws IOException { - if (hasKeys) { - writer.append(','); - } else { - hasKeys = true; - } - } - - private void writeKey(String key) throws IOException { - optionalComma(); - writer.append(quote(escape(key))); - writer.append(':'); - } - - private void write(String key, String value) throws IOException { - writeKey(key); - writer.append(value); - } - - } - - public final class JsonArrayBuilder { - private final JsonObjectBuilder parent; - - private boolean hasValues; - - public JsonArrayBuilder(JsonObjectBuilder parent) throws IOException { - writer.append('['); - this.parent = parent; - } - - public JsonArrayBuilder value(String value) throws IOException { - optionalComma(); - writer.append(encode(value)); - return this; - } - - public JsonArrayBuilder value(int value) throws IOException { - optionalComma(); - writer.append(encode(value)); - return this; - } - - public JsonArrayBuilder value(long value) throws IOException { - optionalComma(); - writer.append(encode(value)); - return this; - } - - public JsonArrayBuilder value(float value) throws IOException { - optionalComma(); - writer.append(encode(value)); - return this; - } - - public JsonArrayBuilder value(double value) throws IOException { - optionalComma(); - writer.append(encode(value)); - return this; - } - - public JsonArrayBuilder value(Number value) throws IOException { - optionalComma(); - writer.append(encode(value)); - return this; - } - - public JsonArrayBuilder value(boolean value) throws IOException { - optionalComma(); - writer.append(encode(value)); - return this; - } - - public JsonArrayBuilder nil() throws IOException { - optionalComma(); - writer.append("null"); - return this; - } - - public JsonObjectBuilder build() throws IOException { - writer.append(']'); - return parent; - } - - //------------------------------------------< private >--- - - private void optionalComma() throws IOException { - if (hasValues) { - writer.append(','); - } else { - hasValues = true; - } - } - } - - /** - * Escape quotes, \, /, \r, \n, \b, \f, \t and other control characters (U+0000 through U+001F). - */ - public static String escape(String string) { - if (string == null) { - return null; - } - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < string.length(); i++) { - char ch = string.charAt(i); - switch (ch) { - case '"': - sb.append("\\\""); - break; - case '\\': - sb.append("\\\\"); - break; - case '\b': - sb.append("\\b"); - break; - case '\f': - sb.append("\\f"); - break; - case '\n': - sb.append("\\n"); - break; - case '\r': - sb.append("\\r"); - break; - case '\t': - sb.append("\\t"); - break; - default: - //Reference: http://www.unicode.org/versions/Unicode5.1.0/ - if (ch >= '\u0000' && ch <= '\u001F' || - ch >= '\u007F' && ch <= '\u009F' || - ch >= '\u2000' && ch <= '\u20FF') { - - String ss = Integer.toHexString(ch); - sb.append("\\u"); - for (int k = 0; k < 4 - ss.length(); k++) { - sb.append('0'); - } - sb.append(ss.toUpperCase()); - } else { - sb.append(ch); - } - } - } - - return sb.toString(); - } - - public static String quote(String string) { - return '"' + string + '"'; - } - - public static String encode(String value) { - return quote(escape(value)); - } - - public static String encode(int value) { - return Integer.toString(value); - } - - public static String encode(long value) { - return Long.toString(value); - } - - public static String encode(float value) { - // FIXME silently losing data, should probably throw an exception instead - return Float.isInfinite(value) || Float.isNaN(value) - ? "null" - : Float.toString(value); - } - - public static String encode(double value) { - // FIXME silently losing data, should probably throw an exception instead - return Double.isInfinite(value) || Double.isNaN(value) - ? "null" - : Double.toString(value); - } - - public static String encode(Number value) { - return value.toString(); - } - - public static String encode(boolean value) { - return Boolean.toString(value); - } - - public static String encode(String[] values) { - if (values.length == 0) { - return "[]"; - } - - StringBuilder sb = new StringBuilder(); - sb.append('['); - for (String value : values) { - sb.append(encode(value)); - sb.append(','); - } - sb.deleteCharAt(sb.length() - 1); - sb.append(']'); - return sb.toString(); - } - - public static String encode(int[] values) { - if (values.length == 0) { - return "[]"; - } - - StringBuilder sb = new StringBuilder(); - sb.append('['); - for (int value : values) { - sb.append(encode(value)); - sb.append(','); - } - sb.deleteCharAt(sb.length() - 1); - sb.append(']'); - return sb.toString(); - } - - public static String encode(long[] values) { - if (values.length == 0) { - return "[]"; - } - - StringBuilder sb = new StringBuilder(); - sb.append('['); - for (long value : values) { - sb.append(encode(value)); - sb.append(','); - } - sb.deleteCharAt(sb.length() - 1); - sb.append(']'); - return sb.toString(); - } - - public static String encode(float[] values) { - if (values.length == 0) { - return "[]"; - } - - StringBuilder sb = new StringBuilder(); - sb.append('['); - for (float value : values) { - sb.append(encode(value)); - sb.append(','); - } - sb.deleteCharAt(sb.length() - 1); - sb.append(']'); - return sb.toString(); - } - - public static String encode(double[] values) { - if (values.length == 0) { - return "[]"; - } - - StringBuilder sb = new StringBuilder(); - sb.append('['); - for (double value : values) { - sb.append(encode(value)); - sb.append(','); - } - sb.deleteCharAt(sb.length() - 1); - sb.append(']'); - return sb.toString(); - } - - public static String encode(Number[] values) { - if (values.length == 0) { - return "[]"; - } - - StringBuilder sb = new StringBuilder(); - sb.append('['); - for (Number value : values) { - sb.append(encode(value)); - sb.append(','); - } - sb.deleteCharAt(sb.length() - 1); - sb.append(']'); - return sb.toString(); - } - - public static String encode(boolean[] values) { - if (values.length == 0) { - return "[]"; - } - - StringBuilder sb = new StringBuilder(); - sb.append('['); - for (boolean value : values) { - sb.append(encode(value)); - sb.append(','); - } - sb.deleteCharAt(sb.length() - 1); - sb.append(']'); - return sb.toString(); - } - -} Index: oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/CoreValueMapper.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/CoreValueMapper.java (revision 1349715) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/CoreValueMapper.java (revision ) @@ -16,17 +16,17 @@ */ package org.apache.jackrabbit.oak.kernel; -import org.apache.jackrabbit.oak.util.JsonBuilder; -import org.apache.jackrabbit.mk.json.JsopReader; -import org.apache.jackrabbit.oak.api.CoreValue; -import org.apache.jackrabbit.oak.api.CoreValueFactory; - -import javax.jcr.PropertyType; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.jcr.PropertyType; + +import org.apache.jackrabbit.mk.json.JsopReader; +import org.apache.jackrabbit.oak.api.CoreValue; +import org.apache.jackrabbit.oak.api.CoreValueFactory; + /** * CoreValueUtil provides methods to convert {@code CoreValue}s to the JSON * representation passed to MicroKernel and vice versa. @@ -57,25 +57,22 @@ * * @param value The core value to be converted. * @return The encoded JSON string. - * @see JsonBuilder#encode(String) - * @see JsonBuilder#encode(long) - * @see JsonBuilder#encode(long) */ public static String toJsonValue(CoreValue value) { String jsonString; switch (value.getType()) { case PropertyType.BOOLEAN: - jsonString = JsonBuilder.encode(value.getBoolean()); + jsonString = Boolean.toString(value.getBoolean()); break; case PropertyType.LONG: - jsonString = JsonBuilder.encode(value.getLong()); + jsonString = Long.toString(value.getLong()); break; case PropertyType.STRING: String str = value.getString(); if (startsWithHint(str)) { jsonString = buildJsonStringWithHint(value); } else { - jsonString = JsonBuilder.encode(value.getString()); + jsonString = jsonEncode(value.getString()); } break; default: @@ -171,7 +168,7 @@ sb.append(TYPE2HINT.get(value.getType())); sb.append(':'); sb.append(value.getString()); - return JsonBuilder.encode(sb.toString()); + return jsonEncode(sb.toString()); } /** @@ -185,6 +182,61 @@ */ private static boolean startsWithHint(String jsonString) { return jsonString.length() >= 4 && jsonString.charAt(3) == ':'; + } + + /** + * Escape quotes, \, /, \r, \n, \b, \f, \t and other control characters + * (U+0000 through U+001F) and surround with double quotes. + */ + private static String jsonEncode(String value) { + if (value == null) { + return null; + } + + StringBuilder sb = new StringBuilder("\""); + for (int i = 0; i < value.length(); i++) { + char ch = value.charAt(i); + switch (ch) { + case '"': + sb.append("\\\""); + break; + case '\\': + sb.append("\\\\"); + break; + case '\b': + sb.append("\\b"); + break; + case '\f': + sb.append("\\f"); + break; + case '\n': + sb.append("\\n"); + break; + case '\r': + sb.append("\\r"); + break; + case '\t': + sb.append("\\t"); + break; + default: + //Reference: http://www.unicode.org/versions/Unicode5.1.0/ + if (ch >= '\u0000' && ch <= '\u001F' || + ch >= '\u007F' && ch <= '\u009F' || + ch >= '\u2000' && ch <= '\u20FF') { + + String ss = Integer.toHexString(ch); + sb.append("\\u"); + for (int k = 0; k < 4 - ss.length(); k++) { + sb.append('0'); + } + sb.append(ss.toUpperCase()); + } else { + sb.append(ch); + } + } + } + + return sb.append('"').toString(); } } \ No newline at end of file Index: oak-core/src/test/java/org/apache/jackrabbit/oak/util/JsonBuilderTest.java =================================================================== --- oak-core/src/test/java/org/apache/jackrabbit/oak/util/JsonBuilderTest.java (revision 1349715) +++ oak-core/src/test/java/org/apache/jackrabbit/oak/util/JsonBuilderTest.java (revision 1349715) @@ -1,207 +0,0 @@ -/* - * 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.jackrabbit.oak.util; - -import junit.framework.Assert; -import org.apache.jackrabbit.oak.util.JsonBuilder.JsonArrayBuilder; -import org.apache.jackrabbit.oak.util.JsonBuilder.JsonObjectBuilder; -import org.json.simple.parser.ContentHandler; -import org.json.simple.parser.JSONParser; -import org.junit.Test; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.StringReader; -import java.io.StringWriter; -import java.math.BigDecimal; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNotNull; - -public class JsonBuilderTest { - - @Test - public void jsonBuilder() throws IOException { - - StringWriter sw = new StringWriter(); - JsonBuilder.create(sw) - .value("foo", "bar") - .value("int", 3) - .value("float", 3f) - .object("obj") - .value("boolean", true) - .nil("nil") - .array("arr") - .value(1) - .value(2.0f) - .value(2.0d) - .value("42") - .build() - .build() - .array("string array", new String[]{"", "1", "foo"}) - .array("int array", new int[]{1, 2, 3}) - .array("long array", new long[]{1, 2, 3}) - .array("float array", new float[]{1, 2, 3}) - .array("double array", new double[]{1, 2, 3}) - .array("boolean array", new boolean[]{true, false}) - .array("number array", new BigDecimal[]{new BigDecimal(21), new BigDecimal(42)}) - .value("some", "more") - .build(); - - String json = sw.toString(); - assertEquals("{\"foo\":\"bar\",\"int\":3,\"float\":3.0,\"obj\":{\"boolean\":true,\"nil\":null," + - "\"arr\":[1,2.0,2.0,\"42\"]},\"string array\":[\"\",\"1\",\"foo\"],\"int array\":[1,2,3]," + - "\"long array\":[1,2,3],\"float array\":[1.0,2.0,3.0],\"double array\":[1.0,2.0,3.0]," + - "\"boolean array\":[true,false],\"number array\":[21,42],\"some\":\"more\"}", json); - } - - @Test - public void escape() throws IOException { - StringWriter sw = new StringWriter(); - JsonBuilder.create(sw) - .value("back\\slash", "\\") - .value("back\\\\slash", "\\\\") - .build(); - - String json = sw.toString(); - assertEquals("{\"back\\\\slash\":\"\\\\\",\"back\\\\\\\\slash\":\"\\\\\\\\\"}", json); - } - - @Test - public void fixedPoint() { - InputStream one = JsonBuilderTest.class.getResourceAsStream("test.json"); - assertNotNull(one); - InputStreamReader isr = new InputStreamReader(one); - - String s1 = fix(isr); - String s2 = fix(s1); - - // fix == fix fix - assertEquals(s1, s2); - } - - //------------------------------------------< private >--- - - private static String fix(Reader reader) { - StringWriter sw = new StringWriter(); - try { - new JSONParser().parse(reader, new JsonHandler(JsonBuilder.create(sw))); - } catch (Exception e) { - throw new RuntimeException(e); - } - return sw.toString(); - } - - private static String fix(String string) { - return fix(new StringReader(string)); - } - - private static class JsonHandler implements ContentHandler { - private JsonObjectBuilder objectBuilder; - private JsonArrayBuilder arrayBuilder; - private String currentKey; - - public JsonHandler(JsonObjectBuilder objectBuilder) { - this.objectBuilder = objectBuilder; - } - - public void startJSON() { - // ignore - } - - public void endJSON() { - // ignore - } - - public boolean startObject() throws IOException { - if (currentKey != null) { - objectBuilder = objectBuilder.object(currentKey); - } - return true; - } - - public boolean endObject() throws IOException { - objectBuilder = objectBuilder.build(); - return true; - } - - public boolean startObjectEntry(String key) throws IOException { - currentKey = key; - return true; - } - - public boolean endObjectEntry() throws IOException { - return true; - } - - public boolean startArray() throws IOException { - arrayBuilder = objectBuilder.array(currentKey); - return true; - } - - public boolean endArray() throws IOException { - objectBuilder = arrayBuilder.build(); - arrayBuilder = null; - return true; - } - - public boolean primitive(Object value) throws IOException { - if (arrayBuilder == null) { - if (value == null) { - objectBuilder.nil(currentKey); - } else if (value instanceof String) { - objectBuilder.value(currentKey, (String) value); - } else if (value instanceof Integer) { - objectBuilder.value(currentKey, ((Integer) value).intValue()); - } else if (value instanceof Long) { - objectBuilder.value(currentKey, ((Long) value).longValue()); - } else if (value instanceof Double) { - objectBuilder.value(currentKey, ((Double) value).doubleValue()); - } else if (value instanceof Float) { - objectBuilder.value(currentKey, ((Float) value).floatValue()); - } else if (value instanceof Boolean) { - objectBuilder.value(currentKey, (Boolean) value); - } else { - Assert.fail(); - } - } else { - if (value == null) { - arrayBuilder.nil(); - } else if (value instanceof String) { - arrayBuilder.value((String) value); - } else if (value instanceof Integer) { - arrayBuilder.value(((Integer) value).intValue()); - } else if (value instanceof Long) { - arrayBuilder.value(((Long) value).longValue()); - } else if (value instanceof Double) { - arrayBuilder.value(((Double) value).doubleValue()); - } else if (value instanceof Float) { - arrayBuilder.value(((Float) value).floatValue()); - } else if (value instanceof Boolean) { - arrayBuilder.value((Boolean) value); - } else { - Assert.fail(); - } - } - return true; - } - } -}