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