Merge "Add a configurable delay to the DMI stub"
[cps.git] / cps-service / src / main / java / org / onap / cps / spi / model / DataNodeBuilder.java
1 /*
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
11  *
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.
18  *
19  *  SPDX-License-Identifier: Apache-2.0
20  *  ============LICENSE_END=========================================================
21  */
22
23 package org.onap.cps.spi.model;
24
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;
31 import java.util.Map;
32 import java.util.Set;
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;
48
49 @Slf4j
50 public class DataNodeBuilder {
51
52     private ContainerNode containerNode;
53     private String xpath;
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;
60
61     /**
62      * To use parent node xpath for creating {@link DataNode}.
63      *
64      * @param parentNodeXpath xpath of a parent node
65      * @return this {@link DataNodeBuilder} object
66      */
67     public DataNodeBuilder withParentNodeXpath(final String parentNodeXpath) {
68         this.parentNodeXpath = parentNodeXpath;
69         return this;
70     }
71
72     /**
73      * To use {@link Collection} of Normalized Nodes for creating {@link DataNode}.
74      *
75      * @param containerNode used for creating the Data Node
76      * @return this {@link DataNodeBuilder} object
77      */
78     public DataNodeBuilder withContainerNode(final ContainerNode containerNode) {
79         this.containerNode = containerNode;
80         return this;
81     }
82
83     /**
84      * To use xpath for creating {@link DataNode}.
85      *
86      * @param xpath for the data node
87      * @return DataNodeBuilder
88      */
89     public DataNodeBuilder withXpath(final String xpath) {
90         this.xpath = xpath;
91         return this;
92     }
93
94     /**
95      * To use dataspace name for creating {@link DataNode}.
96      *
97      * @param dataspaceName dataspace name for the data node
98      * @return DataNodeBuilder
99      */
100     public DataNodeBuilder withDataspace(final String dataspaceName) {
101         this.dataspaceName = dataspaceName;
102         return this;
103     }
104
105     /**
106      * To use anchor name for creating {@link DataNode}.
107      *
108      * @param anchorName anchor name for the data node
109      * @return DataNodeBuilder
110      */
111     public DataNodeBuilder withAnchor(final String anchorName) {
112         this.anchorName = anchorName;
113         return this;
114     }
115
116     /**
117      * To use module name for prefix for creating {@link DataNode}.
118      *
119      * @param moduleNamePrefix module name as prefix
120      * @return DataNodeBuilder
121      */
122     public DataNodeBuilder withModuleNamePrefix(final String moduleNamePrefix) {
123         this.moduleNamePrefix = moduleNamePrefix;
124         return this;
125     }
126
127     /**
128      * To use attributes for creating {@link DataNode}.
129      *
130      * @param leaves for the data node
131      * @return DataNodeBuilder
132      */
133     public DataNodeBuilder withLeaves(final Map<String, Serializable> leaves) {
134         this.leaves = leaves;
135         return this;
136     }
137
138     /**
139      * To specify child nodes needs to be used while creating {@link DataNode}.
140      *
141      * @param childDataNodes to be added to the dataNode
142      * @return DataNodeBuilder
143      */
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;
148         return this;
149     }
150
151     /**
152      * To create the {@link DataNode}.
153      *
154      * @return {@link DataNode}
155      */
156     public DataNode build() {
157         if (containerNode != null) {
158             return buildFromContainerNode();
159         }
160         return buildFromAttributes();
161     }
162
163     /**
164      * To build a {@link Collection} of {@link DataNode} objects.
165      *
166      * @return {@link DataNode} {@link Collection}
167      */
168     public Collection<DataNode> buildCollection() {
169         if (containerNode != null) {
170             return buildCollectionFromContainerNode();
171         }
172         return Collections.emptySet();
173     }
174
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);
183         return dataNode;
184     }
185
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");
190         }
191         return dataNodeCollection.iterator().next();
192     }
193
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);
199             }
200         }
201         return parentDataNode.getChildDataNodes();
202     }
203
204     private static void addDataNodeFromNormalizedNode(final DataNode currentDataNode,
205         final NormalizedNode normalizedNode) {
206
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);
219         } else {
220             log.warn("Unsupported NormalizedNode type detected: {}", normalizedNode.getClass());
221         }
222     }
223
224     private static void addYangContainer(final DataNode currentDataNode, final DataContainerNode dataContainerNode) {
225         final DataNode dataContainerDataNode =
226             (dataContainerNode.getIdentifier() instanceof YangInstanceIdentifier.AugmentationIdentifier)
227                 ? currentDataNode
228                 : createAndAddChildDataNode(currentDataNode, YangUtils.buildXpath(dataContainerNode.getIdentifier()));
229         final Collection<DataContainerChild> normalizedChildNodes = dataContainerNode.body();
230         for (final NormalizedNode normalizedNode : normalizedChildNodes) {
231             addDataNodeFromNormalizedNode(dataContainerDataNode, normalizedNode);
232         }
233     }
234
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)
240             .build();
241         currentDataNode.setLeaves(leaves);
242     }
243
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())
247                 .stream()
248                 .map(NormalizedNode::body)
249                 .collect(Collectors.toList());
250         if (leafSetNode.ordering() == Ordering.SYSTEM) {
251             leafListValues.sort(null);
252         }
253         leafListValues = Collections.unmodifiableList(leafListValues);
254         addYangLeaf(currentDataNode, leafListName, (Serializable) leafListValues);
255     }
256
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);
261         }
262     }
263
264     private static DataNode createAndAddChildDataNode(final DataNode parentDataNode, final String childXpath) {
265
266         final var newChildDataNode = new DataNodeBuilder()
267             .withXpath(parentDataNode.getXpath() + childXpath)
268             .build();
269         final Set<DataNode> allChildDataNodes = new ImmutableSet.Builder<DataNode>()
270             .addAll(parentDataNode.getChildDataNodes())
271             .add(newChildDataNode)
272             .build();
273         parentDataNode.setChildDataNodes(allChildDataNodes);
274         return newChildDataNode;
275     }
276
277     private static void addChoiceNode(final DataNode currentDataNode, final ChoiceNode choiceNode) {
278
279         final Collection<DataContainerChild> normalizedChildNodes = choiceNode.body();
280         for (final NormalizedNode normalizedNode : normalizedChildNodes) {
281             addDataNodeFromNormalizedNode(currentDataNode, normalizedNode);
282         }
283     }
284
285 }