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