b040af5bb43858f0d14fbc5ead8e132801183f03
[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-2023 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.data.api.YangInstanceIdentifier;
38 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
40 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
41 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
42 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
43 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
44 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
45 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
46 import org.opendaylight.yangtools.yang.data.api.schema.ValueNode;
47
48 @Slf4j
49 public class DataNodeBuilder {
50
51     private ContainerNode containerNode;
52     private String xpath;
53     private String moduleNamePrefix;
54     private String parentNodeXpath = "";
55     private Map<String, Serializable> leaves = Collections.emptyMap();
56     private Collection<DataNode> childDataNodes = Collections.emptySet();
57     private String dataspaceName;
58     private String anchorName;
59
60     /**
61      * To use parent node xpath for creating {@link DataNode}.
62      *
63      * @param parentNodeXpath xpath of a parent node
64      * @return this {@link DataNodeBuilder} object
65      */
66     public DataNodeBuilder withParentNodeXpath(final String parentNodeXpath) {
67         this.parentNodeXpath = parentNodeXpath;
68         return this;
69     }
70
71     /**
72      * To use {@link Collection} of Normalized Nodes for creating {@link DataNode}.
73      *
74      * @param containerNode used for creating the Data Node
75      * @return this {@link DataNodeBuilder} object
76      */
77     public DataNodeBuilder withContainerNode(final ContainerNode containerNode) {
78         this.containerNode = containerNode;
79         return this;
80     }
81
82     /**
83      * To use xpath for creating {@link DataNode}.
84      *
85      * @param xpath for the data node
86      * @return DataNodeBuilder
87      */
88     public DataNodeBuilder withXpath(final String xpath) {
89         this.xpath = xpath;
90         return this;
91     }
92
93     /**
94      * To use dataspace name for creating {@link DataNode}.
95      *
96      * @param dataspaceName dataspace name for the data node
97      * @return DataNodeBuilder
98      */
99     public DataNodeBuilder withDataspace(final String dataspaceName) {
100         this.dataspaceName = dataspaceName;
101         return this;
102     }
103
104     /**
105      * To use anchor name for creating {@link DataNode}.
106      *
107      * @param anchorName anchor name for the data node
108      * @return DataNodeBuilder
109      */
110     public DataNodeBuilder withAnchor(final String anchorName) {
111         this.anchorName = anchorName;
112         return this;
113     }
114
115     /**
116      * To use module name for prefix for creating {@link DataNode}.
117      *
118      * @param moduleNamePrefix module name as prefix
119      * @return DataNodeBuilder
120      */
121     public DataNodeBuilder withModuleNamePrefix(final String moduleNamePrefix) {
122         this.moduleNamePrefix = moduleNamePrefix;
123         return this;
124     }
125
126     /**
127      * To use attributes for creating {@link DataNode}.
128      *
129      * @param leaves for the data node
130      * @return DataNodeBuilder
131      */
132     public DataNodeBuilder withLeaves(final Map<String, Serializable> leaves) {
133         this.leaves = leaves;
134         return this;
135     }
136
137     /**
138      * To specify child nodes needs to be used while creating {@link DataNode}.
139      *
140      * @param childDataNodes to be added to the dataNode
141      * @return DataNodeBuilder
142      */
143     public DataNodeBuilder withChildDataNodes(final Collection<DataNode> childDataNodes) {
144         // Added as this is being set from test cases .
145         // Open for suggestions
146         this.childDataNodes = childDataNodes;
147         return this;
148     }
149
150     /**
151      * To create the {@link DataNode}.
152      *
153      * @return {@link DataNode}
154      */
155     public DataNode build() {
156         if (containerNode != null) {
157             return buildFromContainerNode();
158         }
159         return buildFromAttributes();
160     }
161
162     /**
163      * To build a {@link Collection} of {@link DataNode} objects.
164      *
165      * @return {@link DataNode} {@link Collection}
166      */
167     public Collection<DataNode> buildCollection() {
168         if (containerNode != null) {
169             return buildCollectionFromContainerNode();
170         }
171         return Collections.emptySet();
172     }
173
174     private DataNode buildFromAttributes() {
175         final var dataNode = new DataNode();
176         dataNode.setXpath(xpath);
177         dataNode.setModuleNamePrefix(moduleNamePrefix);
178         dataNode.setLeaves(leaves);
179         dataNode.setChildDataNodes(childDataNodes);
180         dataNode.setDataspace(dataspaceName);
181         dataNode.setAnchorName(anchorName);
182         return dataNode;
183     }
184
185     private DataNode buildFromContainerNode() {
186         final Collection<DataNode> dataNodeCollection = buildCollectionFromContainerNode();
187         if (dataNodeCollection.isEmpty()) {
188             throw new DataValidationException("Unsupported Normalized Node", "No valid node found");
189         }
190         return dataNodeCollection.iterator().next();
191     }
192
193     private Collection<DataNode> buildCollectionFromContainerNode() {
194         final var parentDataNode = new DataNodeBuilder().withXpath(parentNodeXpath).build();
195         if (containerNode.body() != null) {
196             for (final NormalizedNode normalizedNode: containerNode.body()) {
197                 addDataNodeFromNormalizedNode(parentDataNode, normalizedNode);
198             }
199         }
200         return parentDataNode.getChildDataNodes();
201     }
202
203     private static void addDataNodeFromNormalizedNode(final DataNode currentDataNode,
204         final NormalizedNode normalizedNode) {
205
206         if (normalizedNode instanceof ChoiceNode) {
207             addChoiceNode(currentDataNode, (ChoiceNode) normalizedNode);
208         } else if (normalizedNode instanceof DataContainerNode) {
209             addYangContainer(currentDataNode, (DataContainerNode) normalizedNode);
210         } else if (normalizedNode instanceof MapNode) {
211             addDataNodeForEachListElement(currentDataNode, (MapNode) normalizedNode);
212         } else if (normalizedNode instanceof ValueNode) {
213             final ValueNode<NormalizedNode> valuesNode = (ValueNode) normalizedNode;
214             addYangLeaf(currentDataNode, valuesNode.getIdentifier().getNodeType().getLocalName(),
215                     (Serializable) valuesNode.body());
216         } else if (normalizedNode instanceof LeafSetNode) {
217             addYangLeafList(currentDataNode, (LeafSetNode<?>) normalizedNode);
218         } else {
219             log.warn("Unsupported NormalizedNode type detected: {}", normalizedNode.getClass());
220         }
221     }
222
223     private static void addYangContainer(final DataNode currentDataNode, final DataContainerNode dataContainerNode) {
224         final DataNode dataContainerDataNode =
225             (dataContainerNode.getIdentifier() instanceof YangInstanceIdentifier.AugmentationIdentifier)
226                 ? currentDataNode
227                 : createAndAddChildDataNode(currentDataNode, YangUtils.buildXpath(dataContainerNode.getIdentifier()));
228         final Collection<DataContainerChild> normalizedChildNodes = dataContainerNode.body();
229         for (final NormalizedNode normalizedNode : normalizedChildNodes) {
230             addDataNodeFromNormalizedNode(dataContainerDataNode, normalizedNode);
231         }
232     }
233
234     private static void addYangLeaf(final DataNode currentDataNode, final String leafName,
235                                     final Serializable leafValue) {
236         final Map<String, Serializable> leaves = new ImmutableMap.Builder<String, Serializable>()
237             .putAll(currentDataNode.getLeaves())
238             .put(leafName, leafValue)
239             .build();
240         currentDataNode.setLeaves(leaves);
241     }
242
243     private static void addYangLeafList(final DataNode currentDataNode, final LeafSetNode<?> leafSetNode) {
244         final String leafListName = leafSetNode.getIdentifier().getNodeType().getLocalName();
245         final List<?> leafListValues = ((Collection<? extends NormalizedNode>) leafSetNode.body())
246                 .stream()
247                 .map(normalizedNode -> (normalizedNode).body())
248                 .collect(Collectors.toUnmodifiableList());
249         addYangLeaf(currentDataNode, leafListName, (Serializable) leafListValues);
250     }
251
252     private static void addDataNodeForEachListElement(final DataNode currentDataNode, final MapNode mapNode) {
253         final Collection<MapEntryNode> mapEntryNodes = mapNode.body();
254         for (final MapEntryNode mapEntryNode : mapEntryNodes) {
255             addDataNodeFromNormalizedNode(currentDataNode, mapEntryNode);
256         }
257     }
258
259     private static DataNode createAndAddChildDataNode(final DataNode parentDataNode, final String childXpath) {
260
261         final var newChildDataNode = new DataNodeBuilder()
262             .withXpath(parentDataNode.getXpath() + childXpath)
263             .build();
264         final Set<DataNode> allChildDataNodes = new ImmutableSet.Builder<DataNode>()
265             .addAll(parentDataNode.getChildDataNodes())
266             .add(newChildDataNode)
267             .build();
268         parentDataNode.setChildDataNodes(allChildDataNodes);
269         return newChildDataNode;
270     }
271
272     private static void addChoiceNode(final DataNode currentDataNode, final ChoiceNode choiceNode) {
273
274         final Collection<DataContainerChild> normalizedChildNodes = choiceNode.body();
275         for (final NormalizedNode normalizedNode : normalizedChildNodes) {
276             addDataNodeFromNormalizedNode(currentDataNode, normalizedNode);
277         }
278     }
279
280 }