Merge "Fixing SonarQube violations"
[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  *  ================================================================================
7  *  Licensed under the Apache License, Version 2.0 (the "License");
8  *  you may not use this file except in compliance with the License.
9  *  You may obtain a copy of the License at
10  *
11  *        http://www.apache.org/licenses/LICENSE-2.0
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  *
18  *  SPDX-License-Identifier: Apache-2.0
19  *  ============LICENSE_END=========================================================
20  */
21
22 package org.onap.cps.spi.model;
23
24 import com.google.common.collect.ImmutableMap;
25 import com.google.common.collect.ImmutableSet;
26 import java.io.Serializable;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32 import java.util.stream.Collectors;
33 import lombok.extern.slf4j.Slf4j;
34 import org.onap.cps.spi.exceptions.DataValidationException;
35 import org.onap.cps.utils.YangUtils;
36 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
37 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
39 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
40 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
41 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
42 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
43 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
44 import org.opendaylight.yangtools.yang.data.api.schema.ValueNode;
45
46 @Slf4j
47 public class DataNodeBuilder {
48
49     private NormalizedNode normalizedNodeTree;
50     private String xpath;
51     private String moduleNamePrefix;
52     private String parentNodeXpath = "";
53     private Map<String, Serializable> leaves = Collections.emptyMap();
54     private Collection<DataNode> childDataNodes = Collections.emptySet();
55
56     /**
57      * To use parent node xpath for creating {@link DataNode}.
58      *
59      * @param parentNodeXpath xpath of a parent node
60      * @return this {@link DataNodeBuilder} object
61      */
62     public DataNodeBuilder withParentNodeXpath(final String parentNodeXpath) {
63         this.parentNodeXpath = parentNodeXpath;
64         return this;
65     }
66
67
68     /**
69      * To use {@link NormalizedNode} for creating {@link DataNode}.
70      *
71      * @param normalizedNodeTree used for creating the Data Node
72      * @return this {@link DataNodeBuilder} object
73      */
74     public DataNodeBuilder withNormalizedNodeTree(final NormalizedNode normalizedNodeTree) {
75         this.normalizedNodeTree = normalizedNodeTree;
76         return this;
77     }
78
79     /**
80      * To use xpath for creating {@link DataNode}.
81      *
82      * @param xpath for the data node
83      * @return DataNodeBuilder
84      */
85     public DataNodeBuilder withXpath(final String xpath) {
86         this.xpath = xpath;
87         return this;
88     }
89
90     /**
91      * To use module name for prefix for creating {@link DataNode}.
92      *
93      * @param moduleNamePrefix module name as prefix
94      * @return DataNodeBuilder
95      */
96     public DataNodeBuilder withModuleNamePrefix(final String moduleNamePrefix) {
97         this.moduleNamePrefix = moduleNamePrefix;
98         return this;
99     }
100
101     /**
102      * To use attributes for creating {@link DataNode}.
103      *
104      * @param leaves for the data node
105      * @return DataNodeBuilder
106      */
107     public DataNodeBuilder withLeaves(final Map<String, Serializable> leaves) {
108         this.leaves = leaves;
109         return this;
110     }
111
112     /**
113      * To specify child nodes needs to be used while creating {@link DataNode}.
114      *
115      * @param childDataNodes to be added to the dataNode
116      * @return DataNodeBuilder
117      */
118     public DataNodeBuilder withChildDataNodes(final Collection<DataNode> childDataNodes) {
119         // Added as this is being set from test cases .
120         // Open for suggestions
121         this.childDataNodes = childDataNodes;
122         return this;
123     }
124
125     /**
126      * To create the {@link DataNode}.
127      *
128      * @return {@link DataNode}
129      */
130     public DataNode build() {
131         if (normalizedNodeTree != null) {
132             return buildFromNormalizedNodeTree();
133         } else {
134             return buildFromAttributes();
135         }
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 (normalizedNodeTree != null) {
145             return buildCollectionFromNormalizedNodeTree();
146         } else {
147             return Set.of(buildFromAttributes());
148         }
149     }
150
151     private DataNode buildFromAttributes() {
152         final var dataNode = new DataNode();
153         dataNode.setXpath(xpath);
154         dataNode.setModuleNamePrefix(moduleNamePrefix);
155         dataNode.setLeaves(leaves);
156         dataNode.setChildDataNodes(childDataNodes);
157         return dataNode;
158     }
159
160     private DataNode buildFromNormalizedNodeTree() {
161         final Collection<DataNode> dataNodeCollection = buildCollectionFromNormalizedNodeTree();
162         if (!dataNodeCollection.iterator().hasNext()) {
163             throw new DataValidationException(
164                 "Unsupported xpath: ", "Unsupported xpath as it is referring to one element");
165         }
166         return dataNodeCollection.iterator().next();
167     }
168
169     private Collection<DataNode> buildCollectionFromNormalizedNodeTree() {
170         final var parentDataNode = new DataNodeBuilder().withXpath(parentNodeXpath).build();
171         addDataNodeFromNormalizedNode(parentDataNode, normalizedNodeTree);
172         return parentDataNode.getChildDataNodes();
173     }
174
175     private static void addDataNodeFromNormalizedNode(final DataNode currentDataNode,
176         final NormalizedNode normalizedNode) {
177
178         if (normalizedNode instanceof ChoiceNode) {
179             addChoiceNode(currentDataNode, (ChoiceNode) normalizedNode);
180         } else if (normalizedNode instanceof DataContainerNode) {
181             addYangContainer(currentDataNode, (DataContainerNode) normalizedNode);
182         } else if (normalizedNode instanceof MapNode) {
183             addDataNodeForEachListElement(currentDataNode, (MapNode) normalizedNode);
184         } else if (normalizedNode instanceof ValueNode) {
185             final ValueNode<NormalizedNode> valuesNode = (ValueNode) normalizedNode;
186             addYangLeaf(currentDataNode, valuesNode.getIdentifier().getNodeType().getLocalName(),
187                     (Serializable) valuesNode.body());
188         } else if (normalizedNode instanceof LeafSetNode) {
189             addYangLeafList(currentDataNode, (LeafSetNode<?>) normalizedNode);
190         } else {
191             log.warn("Unsupported NormalizedNode type detected: {}", normalizedNode.getClass());
192         }
193     }
194
195     private static void addYangContainer(final DataNode currentDataNode, final DataContainerNode dataContainerNode) {
196         final DataNode dataContainerDataNode =
197             (dataContainerNode.getIdentifier() instanceof YangInstanceIdentifier.AugmentationIdentifier)
198                 ? currentDataNode
199                 : createAndAddChildDataNode(currentDataNode, YangUtils.buildXpath(dataContainerNode.getIdentifier()));
200         final Collection<DataContainerChild> normalizedChildNodes = dataContainerNode.body();
201         for (final NormalizedNode normalizedNode : normalizedChildNodes) {
202             addDataNodeFromNormalizedNode(dataContainerDataNode, normalizedNode);
203         }
204     }
205
206     private static void addYangLeaf(final DataNode currentDataNode, final String leafName,
207                                     final Serializable leafValue) {
208         final Map<String, Serializable> leaves = new ImmutableMap.Builder<String, Serializable>()
209             .putAll(currentDataNode.getLeaves())
210             .put(leafName, leafValue)
211             .build();
212         currentDataNode.setLeaves(leaves);
213     }
214
215     private static void addYangLeafList(final DataNode currentDataNode, final LeafSetNode<?> leafSetNode) {
216         final String leafListName = leafSetNode.getIdentifier().getNodeType().getLocalName();
217         final List<?> leafListValues = ((Collection<? extends NormalizedNode>) leafSetNode.body())
218                 .stream()
219                 .map(normalizedNode -> (normalizedNode).body())
220                 .collect(Collectors.toUnmodifiableList());
221         addYangLeaf(currentDataNode, leafListName, (Serializable) leafListValues);
222     }
223
224     private static void addDataNodeForEachListElement(final DataNode currentDataNode, final MapNode mapNode) {
225         final Collection<MapEntryNode> mapEntryNodes = mapNode.body();
226         for (final MapEntryNode mapEntryNode : mapEntryNodes) {
227             addDataNodeFromNormalizedNode(currentDataNode, mapEntryNode);
228         }
229     }
230
231     private static DataNode createAndAddChildDataNode(final DataNode parentDataNode, final String childXpath) {
232
233         final var newChildDataNode = new DataNodeBuilder()
234             .withXpath(parentDataNode.getXpath() + childXpath)
235             .build();
236         final Set<DataNode> allChildDataNodes = new ImmutableSet.Builder<DataNode>()
237             .addAll(parentDataNode.getChildDataNodes())
238             .add(newChildDataNode)
239             .build();
240         parentDataNode.setChildDataNodes(allChildDataNodes);
241         return newChildDataNode;
242     }
243
244     private static void addChoiceNode(final DataNode currentDataNode, final ChoiceNode choiceNode) {
245
246         final Collection<DataContainerChild> normalizedChildNodes = choiceNode.body();
247         for (final NormalizedNode normalizedNode : normalizedChildNodes) {
248             addDataNodeFromNormalizedNode(currentDataNode, normalizedNode);
249         }
250     }
251
252
253 }