diff options
author | Filip Krzywka <filip.krzywka@nokia.com> | 2019-02-19 13:33:45 +0100 |
---|---|---|
committer | Filip Krzywka <filip.krzywka@nokia.com> | 2019-02-20 11:48:44 +0000 |
commit | fc88ac806f5650ec9f70c4849e3815132038ec5c (patch) | |
tree | 29a280701d5c5d532cd5a8b43a152b3a6fe53df8 | |
parent | 5020b150b594e4bb972a136156f744398eff8fd1 (diff) |
Parse JsonObject to MerkleTree
Change-Id: I424eec2c4c47ddff1bff3ef612a7b31a62e1cf3e
Issue-ID: DCAEGEN2-1254
Signed-off-by: Filip Krzywka <filip.krzywka@nokia.com>
2 files changed, 288 insertions, 0 deletions
diff --git a/rest-services/cbs-client/src/main/java/org/onap/dcaegen2/services/sdk/rest/services/cbs/client/api/listener/MerkleTreeParser.java b/rest-services/cbs-client/src/main/java/org/onap/dcaegen2/services/sdk/rest/services/cbs/client/api/listener/MerkleTreeParser.java new file mode 100644 index 00000000..15c4eea2 --- /dev/null +++ b/rest-services/cbs-client/src/main/java/org/onap/dcaegen2/services/sdk/rest/services/cbs/client/api/listener/MerkleTreeParser.java @@ -0,0 +1,129 @@ +/* + * ============LICENSE_START==================================== + * DCAEGEN2-SERVICES-SDK + * ========================================================= + * Copyright (C) 2019 Nokia. All rights reserved. + * ========================================================= + * 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. + * ============LICENSE_END===================================== + */ +package org.onap.dcaegen2.services.sdk.rest.services.cbs.client.api.listener; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import io.vavr.collection.List; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import static java.lang.String.valueOf; + + +/** + * Class responsible for creating MerkleTree structure from JsonObject. + * + * @since 1.1.2 + */ +class MerkleTreeParser { + + /** + * <p> Method used to parse {@link JsonObject} into {@link MerkleTree} structure.</p> + * <p> The algorithm will recursively create mapping of (path in tree)->(value) from JsonObject + * and use it to create MerkleTree by means of {@link MerkleTree#add(List, Object)} method. </p> + * <p> Each JsonObject will append it's key to path until value of primitive type is encountered. + * For each JsonArray element artificial path is created by creating lables from sequential integers. + * This results in array split into multiple nodes in MerkleTree structure.</p> + * + * <p>Example. For JsonObject: + * <pre> + * { + * "p1": "v1", + * "p2": ["v2", "v3"] + * "p3": { + * "p4": "v4" + * } + * } + * </pre> + * following map would be created:</p> + * <pre> + * "v1" <- ["p1"] + * "v2" <- ["p2", "0"] + * "v3" <- ["p2", "1"] + * "v4" <- ["p3", "p4"] + * </pre> + * + * @param json JsonObject to be parsed + * @since 1.1.2 + */ + MerkleTree<String> fromJsonObject(final @NotNull JsonObject json) { + MerkleTree<String> tree = MerkleTree.emptyWithDefaultDigest(String::getBytes); + for (Entry<String, JsonElement> entry : json.entrySet()) { + tree = treeEnhancedWithEntry(tree, entry); + } + + return tree; + } + + private MerkleTree<String> treeEnhancedWithEntry(final MerkleTree<String> tree, + final Entry<String, JsonElement> entry) { + return createTreeFromValuesPaths(tree, pathsToValues(entry, List.empty())); + } + + private Map<List<String>, String> pathsToValues(Entry<String, JsonElement> entry, List<String> elementPathPrefix) { + return pathsToValuesFromJsonElement(entry.getKey(), entry.getValue(), elementPathPrefix); + } + + private Map<List<String>, String> pathsToValuesFromJsonElement(final String jsonKey, + final JsonElement element, + final List<String> elementPathPrefix) { + final HashMap<List<String>, String> pathToValue = new HashMap<>(); + final List<String> newPrefix = elementPathPrefix.append(jsonKey); + + if (element.isJsonObject()) { + element.getAsJsonObject() + .entrySet() + .forEach(entry -> pathToValue.putAll(pathsToValues(entry, newPrefix))); + } else if (element.isJsonArray()) { + pathToValue.putAll(handleArray(newPrefix, element.getAsJsonArray())); + } else if (element.isJsonPrimitive()) { + pathToValue.put(newPrefix, element.getAsString()); + } else if (element.isJsonNull()) { + pathToValue.put(newPrefix, null); + } + return pathToValue; + } + + private HashMap<List<String>, String> handleArray(List<String> newPrefix, JsonArray jsonArray) { + final HashMap<List<String>, String> hashMap = new HashMap<>(); + int labelIndex = 0; + + for (JsonElement jsonElement : jsonArray) { + String jsonKey = valueOf(labelIndex++); + hashMap.putAll(pathsToValuesFromJsonElement(jsonKey, jsonElement, newPrefix)); + } + return hashMap; + } + + private MerkleTree<String> createTreeFromValuesPaths(MerkleTree<String> tree, + final Map<List<String>, String> pathToValue) { + for (Entry<List<String>, String> entry : pathToValue.entrySet()) { + List<String> path = entry.getKey(); + String value = entry.getValue(); + tree = tree.add(path, value); + } + return tree; + } +} diff --git a/rest-services/cbs-client/src/test/java/org/onap/dcaegen2/services/sdk/rest/services/cbs/client/api/listener/MerkleTreeParserTest.java b/rest-services/cbs-client/src/test/java/org/onap/dcaegen2/services/sdk/rest/services/cbs/client/api/listener/MerkleTreeParserTest.java new file mode 100644 index 00000000..080f8097 --- /dev/null +++ b/rest-services/cbs-client/src/test/java/org/onap/dcaegen2/services/sdk/rest/services/cbs/client/api/listener/MerkleTreeParserTest.java @@ -0,0 +1,159 @@ +/* + * ============LICENSE_START==================================== + * DCAEGEN2-SERVICES-SDK + * ========================================================= + * Copyright (C) 2019 Nokia. All rights reserved. + * ========================================================= + * 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. + * ============LICENSE_END===================================== + */ + +package org.onap.dcaegen2.services.sdk.rest.services.cbs.client.api.listener; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; + +import java.math.BigInteger; + + +import static org.assertj.core.api.Assertions.assertThat; + +class MerkleTreeParserTest { + + private final MerkleTreeParser cut = new MerkleTreeParser(); + + @Test + void fromJsonObject_givenEmptyJsonObject_shouldReturnEmptyMerkleTree() { + JsonObject jsonObject = new JsonObject(); + + MerkleTree<String> tree = cut.fromJsonObject(jsonObject); + + assertThat(tree.isSame(emptyTree())).isTrue(); + } + + @Test + void fromJsonObject_givenSingleKeyValuePair_shouldReturnSingleNodeTree() { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("p1", "v1"); + + MerkleTree<String> tree = cut.fromJsonObject(jsonObject); + + MerkleTree<String> expected = emptyTree() + .add("v1", "p1"); + assertThat(tree).isEqualTo(expected); + } + + @Test + void fromJsonObject_givenSingleKeyValuePair_atDeeperPathLevel_shouldReturnTreeWithSingleLeafAndCorrectNodesOnTheWay() { + JsonObject singleKeyValuePair = new JsonObject(); + singleKeyValuePair.addProperty("p3", "v1"); + JsonObject intermediateNode = new JsonObject(); + intermediateNode.add("p2", singleKeyValuePair); + JsonObject jsonObject = new JsonObject(); + jsonObject.add("p1", intermediateNode); + + MerkleTree<String> tree = cut.fromJsonObject(jsonObject); + + MerkleTree<String> expected = emptyTree() + .add("v1", "p1", "p2", "p3"); + assertThat(tree).isEqualTo(expected); + } + + @Test + void fromJsonObject_givenMultipleKeyValuePairs_shouldCreateMultipleLeafs() { + JsonObject keyValuePairs = new JsonObject(); + keyValuePairs.addProperty("A", "vA"); + keyValuePairs.addProperty("B", "vB"); + JsonObject jsonObject = new JsonObject(); + jsonObject.add("p1", keyValuePairs); + + MerkleTree<String> tree = cut.fromJsonObject(jsonObject); + + MerkleTree<String> expected = emptyTree() + .add("vA", "p1", "A") + .add("vB", "p1", "B"); + assertThat(tree).isEqualTo(expected); + } + + @Test + void fromJsonObject_givenJsonArray_shouldCreateMultipleLeafsUnderArtificialNodes() { + JsonObject singleKeyValuePair = new JsonObject(); + singleKeyValuePair.addProperty("p2", "v2"); + JsonArray jsonArray = new JsonArray(); + jsonArray.add("v1"); + jsonArray.add(singleKeyValuePair); + JsonObject jsonObject = new JsonObject(); + jsonObject.add("p1", jsonArray); + + MerkleTree<String> tree = cut.fromJsonObject(jsonObject); + + MerkleTree<String> expected = emptyTree() + .add("v1", "p1", "0") + .add("v2", "p1", "1", "p2"); + assertThat(tree).isEqualTo(expected); + } + + + @Test + void fromJsonObject_givenMoreComplicatedJson_shouldReturnCorrectTree() { + // below example is contained in javadoc for method + JsonObject jsonObject2 = new JsonObject(); + jsonObject2.addProperty("p4", "v4"); + JsonArray jsonArray = new JsonArray(); + jsonArray.add("v2"); + jsonArray.add("v3"); + + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("p1", "v1"); + jsonObject.add("p2", jsonArray); + jsonObject.add("p3", jsonObject2); + + MerkleTree<String> tree = cut.fromJsonObject(jsonObject); + + MerkleTree<String> expected = emptyTree() + .add("v1", "p1") + .add("v2", "p2", "0") + .add("v3", "p2", "1") + .add("v4", "p3", "p4"); + assertThat(tree).isEqualTo(expected); + } + + @Test + void fromJsonObject_givenNotStringValues_shouldCastAllToString() { + JsonArray jsonArray = new JsonArray(); + jsonArray.add(1); + jsonArray.add(2L); + jsonArray.add(3.0); + jsonArray.add(true); + jsonArray.add(new BigInteger("999799799799799")); + JsonObject jsonObject = new JsonObject(); + jsonObject.add("p1", jsonArray); + + MerkleTree<String> tree = cut.fromJsonObject(jsonObject); + + MerkleTree<String> expected = emptyTree() + .add("1", "p1", "0") + .add("2", "p1", "1") + .add("3.0", "p1", "2") + .add("true", "p1", "3") + .add("999799799799799", "p1", "4"); + assertThat(tree).isEqualTo(expected); + } + + @NotNull + private MerkleTree<String> emptyTree() { + return MerkleTree.emptyWithDefaultDigest(String::getBytes); + } +}
\ No newline at end of file |