Add module name to cps core output
[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.util.Collection;
27 import java.util.Collections;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.stream.Collectors;
32 import lombok.extern.slf4j.Slf4j;
33 import org.onap.cps.spi.exceptions.DataValidationException;
34 import org.onap.cps.utils.YangUtils;
35 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
36 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
37 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
40 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
41 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
42 import org.opendaylight.yangtools.yang.data.api.schema.ValueNode;
43
44 @Slf4j
45 public class DataNodeBuilder {
46
47     private NormalizedNode<?, ?> normalizedNodeTree;
48     private String xpath;
49     private String moduleNamePrefix;
50     private String parentNodeXpath = "";
51     private Map<String, Object> leaves = Collections.emptyMap();
52     private Collection<DataNode> childDataNodes = Collections.emptySet();
53
54     /**
55      * To use parent node xpath for creating {@link DataNode}.
56      *
57      * @param parentNodeXpath xpath of a parent node
58      * @return this {@link DataNodeBuilder} object
59      */
60     public DataNodeBuilder withParentNodeXpath(final String parentNodeXpath) {
61         this.parentNodeXpath = parentNodeXpath;
62         return this;
63     }
64
65
66     /**
67      * To use {@link NormalizedNode} for creating {@link DataNode}.
68      *
69      * @param normalizedNodeTree used for creating the Data Node
70      * @return this {@link DataNodeBuilder} object
71      */
72     public DataNodeBuilder withNormalizedNodeTree(final NormalizedNode<?, ?> normalizedNodeTree) {
73         this.normalizedNodeTree = normalizedNodeTree;
74         return this;
75     }
76
77     /**
78      * To use xpath for creating {@link DataNode}.
79      *
80      * @param xpath for the data node
81      * @return DataNodeBuilder
82      */
83     public DataNodeBuilder withXpath(final String xpath) {
84         this.xpath = xpath;
85         return this;
86     }
87
88     /**
89      * To use module name for prefix for creating {@link DataNode}.
90      *
91      * @param moduleNamePrefix module name as prefix
92      * @return DataNodeBuilder
93      */
94     public DataNodeBuilder withModuleNamePrefix(final String moduleNamePrefix) {
95         this.moduleNamePrefix = moduleNamePrefix;
96         return this;
97     }
98
99     /**
100      * To use attributes for creating {@link DataNode}.
101      *
102      * @param leaves for the data node
103      * @return DataNodeBuilder
104      */
105     public DataNodeBuilder withLeaves(final Map<String, Object> leaves) {
106         this.leaves = leaves;
107         return this;
108     }
109
110     /**
111      * To specify child nodes needs to be used while creating {@link DataNode}.
112      *
113      * @param childDataNodes to be added to the dataNode
114      * @return DataNodeBuilder
115      */
116     public DataNodeBuilder withChildDataNodes(final Collection<DataNode> childDataNodes) {
117         // Added as this is being set from test cases .
118         // Open for suggestions
119         this.childDataNodes = childDataNodes;
120         return this;
121     }
122
123     /**
124      * To create the {@link DataNode}.
125      *
126      * @return {@link DataNode}
127      */
128     public DataNode build() {
129         if (normalizedNodeTree != null) {
130             return buildFromNormalizedNodeTree();
131         } else {
132             return buildFromAttributes();
133         }
134     }
135
136     /**
137      * To build a {@link Collection} of {@link DataNode} objects.
138      *
139      * @return {@link DataNode} {@link Collection}
140      */
141     public Collection<DataNode> buildCollection() {
142         if (normalizedNodeTree != null) {
143             return buildCollectionFromNormalizedNodeTree();
144         } else {
145             return Set.of(buildFromAttributes());
146         }
147     }
148
149     private DataNode buildFromAttributes() {
150         final var dataNode = new DataNode();
151         dataNode.setXpath(xpath);
152         dataNode.setModuleNamePrefix(moduleNamePrefix);
153         dataNode.setLeaves(leaves);
154         dataNode.setChildDataNodes(childDataNodes);
155         return dataNode;
156     }
157
158     private DataNode buildFromNormalizedNodeTree() {
159         final Collection<DataNode> dataNodeCollection = buildCollectionFromNormalizedNodeTree();
160         if (!dataNodeCollection.iterator().hasNext()) {
161             throw new DataValidationException(
162                 "Unsupported xpath: ", "Unsupported xpath as it is referring to one element");
163         }
164         return dataNodeCollection.iterator().next();
165     }
166
167     private Collection<DataNode> buildCollectionFromNormalizedNodeTree() {
168         final var parentDataNode = new DataNodeBuilder().withXpath(parentNodeXpath).build();
169         addDataNodeFromNormalizedNode(parentDataNode, normalizedNodeTree);
170         return parentDataNode.getChildDataNodes();
171     }
172
173     private static void addDataNodeFromNormalizedNode(final DataNode currentDataNode,
174         final NormalizedNode<?, ?> normalizedNode) {
175
176         if (normalizedNode instanceof DataContainerNode) {
177             addYangContainer(currentDataNode, (DataContainerNode<?>) normalizedNode);
178         } else if (normalizedNode instanceof MapNode) {
179             addDataNodeForEachListElement(currentDataNode, (MapNode) normalizedNode);
180         } else if (normalizedNode instanceof ValueNode) {
181             final ValueNode<?, ?> valuesNode = (ValueNode<?, ?>) normalizedNode;
182             addYangLeaf(currentDataNode, valuesNode.getNodeType().getLocalName(), valuesNode.getValue());
183         } else if (normalizedNode instanceof LeafSetNode) {
184             addYangLeafList(currentDataNode, (LeafSetNode<?>) normalizedNode);
185         } else {
186             log.warn("Unsupported NormalizedNode type detected: {}", normalizedNode.getClass());
187         }
188     }
189
190     private static void addYangContainer(final DataNode currentDataNode, final DataContainerNode<?> dataContainerNode) {
191         final DataNode dataContainerDataNode =
192             (dataContainerNode.getIdentifier() instanceof YangInstanceIdentifier.AugmentationIdentifier)
193                 ? currentDataNode
194                 : createAndAddChildDataNode(currentDataNode, YangUtils.buildXpath(dataContainerNode.getIdentifier()));
195         final Collection<DataContainerChild<?, ?>> normalizedChildNodes = dataContainerNode.getValue();
196         for (final NormalizedNode<?, ?> normalizedNode : normalizedChildNodes) {
197             addDataNodeFromNormalizedNode(dataContainerDataNode, normalizedNode);
198         }
199     }
200
201     private static void addYangLeaf(final DataNode currentDataNode, final String leafName, final Object leafValue) {
202         final Map<String, Object> leaves = new ImmutableMap.Builder<String, Object>()
203             .putAll(currentDataNode.getLeaves())
204             .put(leafName, leafValue)
205             .build();
206         currentDataNode.setLeaves(leaves);
207     }
208
209     private static void addYangLeafList(final DataNode currentDataNode, final LeafSetNode<?> leafSetNode) {
210         final String leafListName = leafSetNode.getNodeType().getLocalName();
211         final List<?> leafListValues = ((Collection<? extends NormalizedNode<?, ?>>) leafSetNode.getValue())
212             .stream()
213             .map(normalizedNode -> ((ValueNode<?, ?>) normalizedNode).getValue())
214             .collect(Collectors.toUnmodifiableList());
215         addYangLeaf(currentDataNode, leafListName, leafListValues);
216     }
217
218     private static void addDataNodeForEachListElement(final DataNode currentDataNode, final MapNode mapNode) {
219         final Collection<MapEntryNode> mapEntryNodes = mapNode.getValue();
220         for (final MapEntryNode mapEntryNode : mapEntryNodes) {
221             addDataNodeFromNormalizedNode(currentDataNode, mapEntryNode);
222         }
223     }
224
225     private static DataNode createAndAddChildDataNode(final DataNode parentDataNode, final String childXpath) {
226
227         final var newChildDataNode = new DataNodeBuilder()
228             .withXpath(parentDataNode.getXpath() + childXpath)
229             .build();
230         final Set<DataNode> allChildDataNodes = new ImmutableSet.Builder<DataNode>()
231             .addAll(parentDataNode.getChildDataNodes())
232             .add(newChildDataNode)
233             .build();
234         parentDataNode.setChildDataNodes(allChildDataNodes);
235         return newChildDataNode;
236     }
237
238 }