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