2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021 Bell Canada. All rights reserved.
4 * Modifications Copyright (C) 2021 Pantheon.tech
5 * Modifications Copyright (C) 2022 Nordix Foundation.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
22 package org.onap.cps.spi.model;
24 import com.google.common.collect.ImmutableMap;
25 import com.google.common.collect.ImmutableSet;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.List;
31 import java.util.stream.Collectors;
32 import lombok.extern.slf4j.Slf4j;
33 import org.onap.cps.spi.exceptions.DataValidationException;
34 import org.onap.cps.utils.YangUtils;
35 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
36 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
37 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
40 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
41 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
42 import org.opendaylight.yangtools.yang.data.api.schema.ValueNode;
45 public class DataNodeBuilder {
47 private NormalizedNode normalizedNodeTree;
49 private String moduleNamePrefix;
50 private String parentNodeXpath = "";
51 private Map<String, Object> leaves = Collections.emptyMap();
52 private Collection<DataNode> childDataNodes = Collections.emptySet();
55 * To use parent node xpath for creating {@link DataNode}.
57 * @param parentNodeXpath xpath of a parent node
58 * @return this {@link DataNodeBuilder} object
60 public DataNodeBuilder withParentNodeXpath(final String parentNodeXpath) {
61 this.parentNodeXpath = parentNodeXpath;
67 * To use {@link NormalizedNode} for creating {@link DataNode}.
69 * @param normalizedNodeTree used for creating the Data Node
70 * @return this {@link DataNodeBuilder} object
72 public DataNodeBuilder withNormalizedNodeTree(final NormalizedNode normalizedNodeTree) {
73 this.normalizedNodeTree = normalizedNodeTree;
78 * To use xpath for creating {@link DataNode}.
80 * @param xpath for the data node
81 * @return DataNodeBuilder
83 public DataNodeBuilder withXpath(final String xpath) {
89 * To use module name for prefix for creating {@link DataNode}.
91 * @param moduleNamePrefix module name as prefix
92 * @return DataNodeBuilder
94 public DataNodeBuilder withModuleNamePrefix(final String moduleNamePrefix) {
95 this.moduleNamePrefix = moduleNamePrefix;
100 * To use attributes for creating {@link DataNode}.
102 * @param leaves for the data node
103 * @return DataNodeBuilder
105 public DataNodeBuilder withLeaves(final Map<String, Object> leaves) {
106 this.leaves = leaves;
111 * To specify child nodes needs to be used while creating {@link DataNode}.
113 * @param childDataNodes to be added to the dataNode
114 * @return DataNodeBuilder
116 public DataNodeBuilder withChildDataNodes(final Collection<DataNode> childDataNodes) {
117 // Added as this is being set from test cases .
118 // Open for suggestions
119 this.childDataNodes = childDataNodes;
124 * To create the {@link DataNode}.
126 * @return {@link DataNode}
128 public DataNode build() {
129 if (normalizedNodeTree != null) {
130 return buildFromNormalizedNodeTree();
132 return buildFromAttributes();
137 * To build a {@link Collection} of {@link DataNode} objects.
139 * @return {@link DataNode} {@link Collection}
141 public Collection<DataNode> buildCollection() {
142 if (normalizedNodeTree != null) {
143 return buildCollectionFromNormalizedNodeTree();
145 return Set.of(buildFromAttributes());
149 private DataNode buildFromAttributes() {
150 final var dataNode = new DataNode();
151 dataNode.setXpath(xpath);
152 dataNode.setModuleNamePrefix(moduleNamePrefix);
153 dataNode.setLeaves(leaves);
154 dataNode.setChildDataNodes(childDataNodes);
158 private DataNode buildFromNormalizedNodeTree() {
159 final Collection<DataNode> dataNodeCollection = buildCollectionFromNormalizedNodeTree();
160 if (!dataNodeCollection.iterator().hasNext()) {
161 throw new DataValidationException(
162 "Unsupported xpath: ", "Unsupported xpath as it is referring to one element");
164 return dataNodeCollection.iterator().next();
167 private Collection<DataNode> buildCollectionFromNormalizedNodeTree() {
168 final var parentDataNode = new DataNodeBuilder().withXpath(parentNodeXpath).build();
169 addDataNodeFromNormalizedNode(parentDataNode, normalizedNodeTree);
170 return parentDataNode.getChildDataNodes();
173 private static void addDataNodeFromNormalizedNode(final DataNode currentDataNode,
174 final NormalizedNode normalizedNode) {
176 if (normalizedNode instanceof DataContainerNode) {
177 addYangContainer(currentDataNode, (DataContainerNode) normalizedNode);
178 } else if (normalizedNode instanceof MapNode) {
179 addDataNodeForEachListElement(currentDataNode, (MapNode) normalizedNode);
180 } else if (normalizedNode instanceof ValueNode) {
181 final ValueNode<NormalizedNode> valuesNode = (ValueNode) normalizedNode;
182 addYangLeaf(currentDataNode, valuesNode.getIdentifier().getNodeType().getLocalName(),
184 } else if (normalizedNode instanceof LeafSetNode) {
185 addYangLeafList(currentDataNode, (LeafSetNode<?>) normalizedNode);
187 log.warn("Unsupported NormalizedNode type detected: {}", normalizedNode.getClass());
191 private static void addYangContainer(final DataNode currentDataNode, final DataContainerNode dataContainerNode) {
192 final DataNode dataContainerDataNode =
193 (dataContainerNode.getIdentifier() instanceof YangInstanceIdentifier.AugmentationIdentifier)
195 : createAndAddChildDataNode(currentDataNode, YangUtils.buildXpath(dataContainerNode.getIdentifier()));
196 final Collection<DataContainerChild> normalizedChildNodes = dataContainerNode.body();
197 for (final NormalizedNode normalizedNode : normalizedChildNodes) {
198 addDataNodeFromNormalizedNode(dataContainerDataNode, normalizedNode);
202 private static void addYangLeaf(final DataNode currentDataNode, final String leafName, final Object leafValue) {
203 final Map<String, Object> leaves = new ImmutableMap.Builder<String, Object>()
204 .putAll(currentDataNode.getLeaves())
205 .put(leafName, leafValue)
207 currentDataNode.setLeaves(leaves);
210 private static void addYangLeafList(final DataNode currentDataNode, final LeafSetNode<?> leafSetNode) {
211 final String leafListName = leafSetNode.getIdentifier().getNodeType().getLocalName();
212 final List<?> leafListValues = ((Collection<? extends NormalizedNode>) leafSetNode.body())
214 .map(normalizedNode -> (normalizedNode).body())
215 .collect(Collectors.toUnmodifiableList());
216 addYangLeaf(currentDataNode, leafListName, leafListValues);
219 private static void addDataNodeForEachListElement(final DataNode currentDataNode, final MapNode mapNode) {
220 final Collection<MapEntryNode> mapEntryNodes = mapNode.body();
221 for (final MapEntryNode mapEntryNode : mapEntryNodes) {
222 addDataNodeFromNormalizedNode(currentDataNode, mapEntryNode);
226 private static DataNode createAndAddChildDataNode(final DataNode parentDataNode, final String childXpath) {
228 final var newChildDataNode = new DataNodeBuilder()
229 .withXpath(parentDataNode.getXpath() + childXpath)
231 final Set<DataNode> allChildDataNodes = new ImmutableSet.Builder<DataNode>()
232 .addAll(parentDataNode.getChildDataNodes())
233 .add(newChildDataNode)
235 parentDataNode.setChildDataNodes(allChildDataNodes);
236 return newChildDataNode;