Browse Source

Utility functions for helping to use elemental.json with Java 8

Change-Id: I7c3cf7be95eaf451be806cb75b7b2a34fc534deb
tags/8.0.0.alpha2
Artur Signell 7 years ago
parent
commit
7c2c1e614a

+ 247
- 0
server/src/main/java/com/vaadin/data/util/JsonUtil.java View File

@@ -0,0 +1,247 @@
/*
* Copyright 2000-2016 Vaadin Ltd.
*
* Licensed 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 com.vaadin.data.util;

import java.util.AbstractList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.DoubleStream;
import java.util.stream.Stream;

import elemental.json.Json;
import elemental.json.JsonArray;
import elemental.json.JsonNumber;
import elemental.json.JsonObject;
import elemental.json.JsonType;
import elemental.json.JsonValue;

/**
* Helpers for using <code>elemental.json</code>.
*
* @author Vaadin Ltd
*/
public class JsonUtil {

/**
* Collects a stream of JSON values to a JSON array.
*/
private static final class JsonArrayCollector
implements Collector<JsonValue, JsonArray, JsonArray> {
@Override
public Supplier<JsonArray> supplier() {
return Json::createArray;
}

@Override
public BiConsumer<JsonArray, JsonValue> accumulator() {
return (array, value) -> array.set(array.length(), value);
}

@Override
public BinaryOperator<JsonArray> combiner() {
return (left, right) -> {
for (int i = 0; i < right.length(); i++) {
left.set(left.length(), right.<JsonValue> get(i));
}
return left;
};
}

@Override
public Function<JsonArray, JsonArray> finisher() {
return Function.identity();
}

@Override
public Set<Collector.Characteristics> characteristics() {
return ARRAY_COLLECTOR_CHARACTERISTICS;
}
}

private static final Set<Collector.Characteristics> ARRAY_COLLECTOR_CHARACTERISTICS = Collections
.unmodifiableSet(
EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));

private JsonUtil() {
// Static-only class
}

/**
* Compares two JSON values for deep equality.
* <p>
* This is a helper for overcoming the fact that {@link JsonValue} doesn't
* override {@link Object#equals(Object)} and
* {@link JsonValue#jsEquals(JsonValue)} is defined to use JavaScript
* semantics where arrays and objects are equals only based on identity.
*
* @param a
* the first JSON value to check, may not be null
* @param b
* the second JSON value to check, may not be null
* @return <code>true</code> if both JSON values are the same;
* <code>false</code> otherwise
*/
public static boolean jsonEquals(JsonValue a, JsonValue b) {
assert a != null;
assert b != null;

if (a == b) {
return true;
}

JsonType type = a.getType();
if (type != b.getType()) {
return false;
}

switch (type) {
case NULL:
return true;
case BOOLEAN:
return a.asBoolean() == b.asBoolean();
case NUMBER:
return Double.doubleToRawLongBits(a.asNumber()) == Double
.doubleToRawLongBits(b.asNumber());
case STRING:
return a.asString().equals(b.asString());
case OBJECT:
return jsonObjectEquals((JsonObject) a, (JsonObject) b);
case ARRAY:
return jsonArrayEquals((JsonArray) a, (JsonArray) b);
default:
throw new IllegalArgumentException("Unsupported JsonType: " + type);
}
}

private static boolean jsonObjectEquals(JsonObject a, JsonObject b) {
assert a != null;
assert b != null;

if (a == b) {
return true;
}

String[] keys = a.keys();

if (keys.length != b.keys().length) {
return false;
}

for (String key : keys) {
JsonValue value = b.get(key);
if (value == null || !jsonEquals(a.get(key), value)) {
return false;
}
}

return true;
}

private static boolean jsonArrayEquals(JsonArray a, JsonArray b) {
assert a != null;
assert b != null;

if (a == b) {
return true;
}

if (a.length() != b.length()) {
return false;
}
for (int i = 0; i < a.length(); i++) {
if (!jsonEquals(a.get(i), b.get(i))) {
return false;
}
}
return true;
}

/**
* Creates a stream from a JSON array.
*
* @param array
* the JSON array to create a stream from
* @return a stream of JSON values
*/
public static <T extends JsonValue> Stream<T> stream(JsonArray array) {
assert array != null;
return new AbstractList<T>() {
@Override
public T get(int index) {
return array.get(index);
}

@Override
public int size() {
return array.length();
}
}.stream();
}

/**
* Creates a stream from a JSON array of objects. This method does not
* verify that all items in the array are actually JSON objects instead of
* some other JSON type.
*
* @param array
* the JSON array to create a stream from
* @return a stream of JSON objects
*/
public static Stream<JsonObject> objectStream(JsonArray array) {
return stream(array);
}

/**
* Creates a double stream from a JSON array of numbers. This method does
* not verify that all items in the array are actually JSON numbers instead
* of some other JSON type.
*
* @param array
* the JSON array to create a stream from
* @return a double stream of the values in the array
*/
public static DoubleStream numberStream(JsonArray array) {
return JsonUtil.<JsonNumber> stream(array)
.mapToDouble(JsonNumber::getNumber);
}

/**
* Creates a collector that collects values into a JSON array.
*
* @return the collector
*/
public static Collector<JsonValue, ?, JsonArray> asArray() {
return new JsonArrayCollector();
}

/**
* Creates a new JSON array with the given values.
*
* @param values
* the values that should be in the created array
* @return the created array
*/
public static JsonArray createArray(JsonValue... values) {
return Stream.of(values).collect(asArray());
}
}

+ 205
- 0
server/src/test/java/com/vaadin/data/util/JsonUtilTest.java View File

@@ -0,0 +1,205 @@
/*
* Copyright 2000-2016 Vaadin Ltd.
*
* Licensed 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 com.vaadin.data.util;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.DoubleStream;
import java.util.stream.Stream;

import org.junit.Assert;
import org.junit.Test;

import elemental.json.Json;
import elemental.json.JsonArray;
import elemental.json.JsonObject;
import elemental.json.JsonValue;
import elemental.json.impl.JreJsonNull;

public class JsonUtilTest {
@Test
public void testEquals() {
// Equal
Assert.assertTrue(
JsonUtil.jsonEquals(Json.create(true), Json.create(true)));
Assert.assertTrue(
JsonUtil.jsonEquals(Json.create("foo"), Json.create("foo")));
Assert.assertTrue(
JsonUtil.jsonEquals(Json.create(3.14), Json.create(3.14)));
Assert.assertTrue(
JsonUtil.jsonEquals(Json.createNull(), Json.createNull()));
Assert.assertTrue(
JsonUtil.jsonEquals(createTestObject1(), createTestObject1()));
Assert.assertTrue(
JsonUtil.jsonEquals(createTestArray1(), createTestArray1()));

// Non-equal with matching types
Assert.assertFalse(
JsonUtil.jsonEquals(Json.create(true), Json.create(false)));
Assert.assertFalse(
JsonUtil.jsonEquals(Json.create("foo"), Json.create("oof")));
Assert.assertFalse(
JsonUtil.jsonEquals(Json.create(3.14), Json.create(3.142)));
Assert.assertFalse(
JsonUtil.jsonEquals(createTestObject1(), createTestObject2()));
Assert.assertFalse(
JsonUtil.jsonEquals(createTestArray1(), createTestArray2()));

// Non-equal with different types
Assert.assertFalse(
JsonUtil.jsonEquals(Json.create(true), Json.create("true")));
Assert.assertFalse(
JsonUtil.jsonEquals(Json.create(3.14), Json.create("3.14")));
Assert.assertFalse(
JsonUtil.jsonEquals(Json.createNull(), Json.create("null")));
Assert.assertFalse(
JsonUtil.jsonEquals(Json.createObject(), Json.create("{}")));
Assert.assertFalse(
JsonUtil.jsonEquals(Json.createArray(), Json.create(0)));
Assert.assertFalse(
JsonUtil.jsonEquals(createTestArray1(), createTestObject1()));
}

@Test(expected = AssertionError.class)
public void testEquals_firstNull_throws() {
JsonUtil.jsonEquals(null, Json.createNull());
}

@Test(expected = AssertionError.class)
public void testEquals_secondNull_throws() {
JsonUtil.jsonEquals(Json.createNull(), null);
}

private static JsonObject createTestObject1() {
JsonObject object = Json.createObject();

object.put("foo", "foo");
object.put("bar", createTestArray1());
object.put("baz", Json.createObject());

return object;
}

private static JsonObject createTestObject2() {
JsonObject object = Json.createObject();

object.put("foo", "oof");
object.put("bar", createTestArray2());
object.put("baz", Json.createArray());

return object;
}

private static JsonArray createTestArray1() {
return Stream.of(Json.create("foo"), Json.createObject())
.collect(JsonUtil.asArray());
}

private static JsonArray createTestArray2() {
return Stream.of(Json.create("bar"), Json.createArray())
.collect(JsonUtil.asArray());
}

@Test
public void collectEmptyStream() {
Stream<JsonValue> jsonValueStream = Stream.empty();
JsonArray a = jsonValueStream.collect(JsonUtil.asArray());
Assert.assertEquals(0, a.length());
}

@Test(expected = AssertionError.class)
public void createObjectStreamForNull() {
JsonUtil.objectStream(null);
}

@Test(expected = AssertionError.class)
public void createNumberStreamForNull() {
JsonUtil.numberStream(null);
}

@Test(expected = AssertionError.class)
public void createStreamForNull() {
JsonUtil.stream(null);
}

@Test
public void testStream() {
JsonArray array = createTestArray1();
List<JsonValue> list = JsonUtil.stream(array)
.collect(Collectors.toList());

Assert.assertEquals(2, list.size());
Assert.assertEquals("foo", list.get(0).asString());
Assert.assertTrue(
JsonUtil.jsonEquals(list.get(1), Json.createObject()));
}

@Test
public void testObjectStream() {
JsonArray array = Stream.of(Json.createObject(), createTestObject1(),
createTestObject2()).collect(JsonUtil.asArray());

List<JsonObject> objects = JsonUtil.objectStream(array)
.collect(Collectors.toList());

Assert.assertEquals(3, objects.size());
Assert.assertTrue(
JsonUtil.jsonEquals(Json.createObject(), objects.get(0)));
Assert.assertTrue(
JsonUtil.jsonEquals(createTestObject1(), objects.get(1)));
Assert.assertTrue(
JsonUtil.jsonEquals(createTestObject2(), objects.get(2)));
}

@Test
public void testNumberStream() {
double[] values = new double[] { 3.14, 42, Double.MAX_VALUE };

JsonArray array = DoubleStream.of(values).mapToObj(Json::create)
.collect(JsonUtil.asArray());

DoubleStream numberStream = JsonUtil.numberStream(array);

Assert.assertArrayEquals(values, numberStream.toArray(), 0);
}

@Test
public void testAsArray() {
Stream<JsonValue> stream = JsonUtil.stream(createTestArray1());

JsonArray array = stream.collect(JsonUtil.asArray());

Assert.assertTrue(JsonUtil.jsonEquals(createTestArray1(), array));
}

@Test
public void testCreateArray() {
JsonArray array = JsonUtil.createArray(Json.create("string"),
Json.createNull());

Assert.assertEquals(2, array.length());
Assert.assertEquals("string", array.getString(0));
Assert.assertSame(JreJsonNull.class, array.get(1).getClass());
}

@Test
public void testCreateEmptyArray() {
JsonArray array = JsonUtil.createArray();

Assert.assertEquals(0, array.length());
}

}

+ 1
- 0
server/src/test/java/com/vaadin/tests/server/ClassesSerializableTest.java View File

@@ -60,6 +60,7 @@ public class ClassesSerializableTest {
"com\\.vaadin\\.buildhelpers.*", //
"com\\.vaadin\\.util\\.ReflectTools.*", //
"com\\.vaadin\\.data\\.util\\.ReflectTools.*", //
"com\\.vaadin\\.data\\.util\\.JsonUtil.*", //
"com\\.vaadin\\.data\\.util.BeanItemContainerGenerator.*",
"com\\.vaadin\\.data\\.util\\.sqlcontainer\\.connection\\.MockInitialContextFactory",
"com\\.vaadin\\.data\\.util\\.sqlcontainer\\.DataGenerator",

Loading…
Cancel
Save