2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021 Bell Canada. All rights reserved.
4 * Modifications Copyright (C) 2021 Pantheon.tech
5 * Modifications Copyright (C) 2022-2024 Nordix Foundation.
6 * Modifications Copyright (C) 2022-2023 TechMahindra Ltd.
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
19 * SPDX-License-Identifier: Apache-2.0
20 * ============LICENSE_END=========================================================
23 package org.onap.cps.spi.model;
25 import com.google.common.collect.ImmutableMap;
26 import com.google.common.collect.ImmutableSet;
27 import java.io.Serializable;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.List;
33 import java.util.stream.Collectors;
34 import lombok.extern.slf4j.Slf4j;
35 import org.onap.cps.spi.exceptions.DataValidationException;
36 import org.onap.cps.utils.YangUtils;
37 import org.opendaylight.yangtools.yang.common.Ordering;
38 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
39 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
40 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
41 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
42 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
43 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
44 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
45 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
46 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
47 import org.opendaylight.yangtools.yang.data.api.schema.ValueNode;
50 public class DataNodeBuilder {
52 private ContainerNode containerNode;
54 private String moduleNamePrefix;
55 private String parentNodeXpath = "";
56 private Map<String, Serializable> leaves = Collections.emptyMap();
57 private Collection<DataNode> childDataNodes = Collections.emptySet();
58 private String dataspaceName;
59 private String anchorName;
62 * To use parent node xpath for creating {@link DataNode}.
64 * @param parentNodeXpath xpath of a parent node
65 * @return this {@link DataNodeBuilder} object
67 public DataNodeBuilder withParentNodeXpath(final String parentNodeXpath) {
68 this.parentNodeXpath = parentNodeXpath;
73 * To use {@link Collection} of Normalized Nodes for creating {@link DataNode}.
75 * @param containerNode used for creating the Data Node
76 * @return this {@link DataNodeBuilder} object
78 public DataNodeBuilder withContainerNode(final ContainerNode containerNode) {
79 this.containerNode = containerNode;
84 * To use xpath for creating {@link DataNode}.
86 * @param xpath for the data node
87 * @return DataNodeBuilder
89 public DataNodeBuilder withXpath(final String xpath) {
95 * To use dataspace name for creating {@link DataNode}.
97 * @param dataspaceName dataspace name for the data node
98 * @return DataNodeBuilder
100 public DataNodeBuilder withDataspace(final String dataspaceName) {
101 this.dataspaceName = dataspaceName;
106 * To use anchor name for creating {@link DataNode}.
108 * @param anchorName anchor name for the data node
109 * @return DataNodeBuilder
111 public DataNodeBuilder withAnchor(final String anchorName) {
112 this.anchorName = anchorName;
117 * To use module name for prefix for creating {@link DataNode}.
119 * @param moduleNamePrefix module name as prefix
120 * @return DataNodeBuilder
122 public DataNodeBuilder withModuleNamePrefix(final String moduleNamePrefix) {
123 this.moduleNamePrefix = moduleNamePrefix;
128 * To use attributes for creating {@link DataNode}.
130 * @param leaves for the data node
131 * @return DataNodeBuilder
133 public DataNodeBuilder withLeaves(final Map<String, Serializable> leaves) {
134 this.leaves = leaves;
139 * To specify child nodes needs to be used while creating {@link DataNode}.
141 * @param childDataNodes to be added to the dataNode
142 * @return DataNodeBuilder
144 public DataNodeBuilder withChildDataNodes(final Collection<DataNode> childDataNodes) {
145 // Added as this is being set from test cases .
146 // Open for suggestions
147 this.childDataNodes = childDataNodes;
152 * To create the {@link DataNode}.
154 * @return {@link DataNode}
156 public DataNode build() {
157 if (containerNode != null) {
158 return buildFromContainerNode();
160 return buildFromAttributes();
164 * To build a {@link Collection} of {@link DataNode} objects.
166 * @return {@link DataNode} {@link Collection}
168 public Collection<DataNode> buildCollection() {
169 if (containerNode != null) {
170 return buildCollectionFromContainerNode();
172 return Collections.emptySet();
175 private DataNode buildFromAttributes() {
176 final var dataNode = new DataNode();
177 dataNode.setXpath(xpath);
178 dataNode.setModuleNamePrefix(moduleNamePrefix);
179 dataNode.setLeaves(leaves);
180 dataNode.setChildDataNodes(childDataNodes);
181 dataNode.setDataspace(dataspaceName);
182 dataNode.setAnchorName(anchorName);
186 private DataNode buildFromContainerNode() {
187 final Collection<DataNode> dataNodeCollection = buildCollectionFromContainerNode();
188 if (dataNodeCollection.isEmpty()) {
189 throw new DataValidationException("Unsupported Normalized Node", "No valid node found");
191 return dataNodeCollection.iterator().next();
194 private Collection<DataNode> buildCollectionFromContainerNode() {
195 final var parentDataNode = new DataNodeBuilder().withXpath(parentNodeXpath).build();
196 if (containerNode.body() != null) {
197 for (final NormalizedNode normalizedNode: containerNode.body()) {
198 addDataNodeFromNormalizedNode(parentDataNode, normalizedNode);
201 return parentDataNode.getChildDataNodes();
204 private static void addDataNodeFromNormalizedNode(final DataNode currentDataNode,
205 final NormalizedNode normalizedNode) {
207 if (normalizedNode instanceof ChoiceNode) {
208 addChoiceNode(currentDataNode, (ChoiceNode) normalizedNode);
209 } else if (normalizedNode instanceof DataContainerNode) {
210 addYangContainer(currentDataNode, (DataContainerNode) normalizedNode);
211 } else if (normalizedNode instanceof MapNode) {
212 addDataNodeForEachListElement(currentDataNode, (MapNode) normalizedNode);
213 } else if (normalizedNode instanceof ValueNode) {
214 final ValueNode<NormalizedNode> valuesNode = (ValueNode) normalizedNode;
215 addYangLeaf(currentDataNode, valuesNode.getIdentifier().getNodeType().getLocalName(),
216 (Serializable) valuesNode.body());
217 } else if (normalizedNode instanceof LeafSetNode) {
218 addYangLeafList(currentDataNode, (LeafSetNode<?>) normalizedNode);
220 log.warn("Unsupported NormalizedNode type detected: {}", normalizedNode.getClass());
224 private static void addYangContainer(final DataNode currentDataNode, final DataContainerNode dataContainerNode) {
225 final DataNode dataContainerDataNode =
226 (dataContainerNode.getIdentifier() instanceof YangInstanceIdentifier.AugmentationIdentifier)
228 : createAndAddChildDataNode(currentDataNode, YangUtils.buildXpath(dataContainerNode.getIdentifier()));
229 final Collection<DataContainerChild> normalizedChildNodes = dataContainerNode.body();
230 for (final NormalizedNode normalizedNode : normalizedChildNodes) {
231 addDataNodeFromNormalizedNode(dataContainerDataNode, normalizedNode);
235 private static void addYangLeaf(final DataNode currentDataNode, final String leafName,
236 final Serializable leafValue) {
237 final Map<String, Serializable> leaves = new ImmutableMap.Builder<String, Serializable>()
238 .putAll(currentDataNode.getLeaves())
239 .put(leafName, leafValue)
241 currentDataNode.setLeaves(leaves);
244 private static void addYangLeafList(final DataNode currentDataNode, final LeafSetNode<?> leafSetNode) {
245 final String leafListName = leafSetNode.getIdentifier().getNodeType().getLocalName();
246 List<?> leafListValues = ((Collection<? extends NormalizedNode>) leafSetNode.body())
248 .map(NormalizedNode::body)
249 .collect(Collectors.toList());
250 if (leafSetNode.ordering() == Ordering.SYSTEM) {
251 leafListValues.sort(null);
253 leafListValues = Collections.unmodifiableList(leafListValues);
254 addYangLeaf(currentDataNode, leafListName, (Serializable) leafListValues);
257 private static void addDataNodeForEachListElement(final DataNode currentDataNode, final MapNode mapNode) {
258 final Collection<MapEntryNode> mapEntryNodes = mapNode.body();
259 for (final MapEntryNode mapEntryNode : mapEntryNodes) {
260 addDataNodeFromNormalizedNode(currentDataNode, mapEntryNode);
264 private static DataNode createAndAddChildDataNode(final DataNode parentDataNode, final String childXpath) {
266 final var newChildDataNode = new DataNodeBuilder()
267 .withXpath(parentDataNode.getXpath() + childXpath)
269 final Set<DataNode> allChildDataNodes = new ImmutableSet.Builder<DataNode>()
270 .addAll(parentDataNode.getChildDataNodes())
271 .add(newChildDataNode)
273 parentDataNode.setChildDataNodes(allChildDataNodes);
274 return newChildDataNode;
277 private static void addChoiceNode(final DataNode currentDataNode, final ChoiceNode choiceNode) {
279 final Collection<DataContainerChild> normalizedChildNodes = choiceNode.body();
280 for (final NormalizedNode normalizedNode : normalizedChildNodes) {
281 addDataNodeFromNormalizedNode(currentDataNode, normalizedNode);