diff options
author | ToineSiebelink <toine.siebelink@est.tech> | 2021-01-25 18:46:20 +0000 |
---|---|---|
committer | puthuparambil.aditya <aditya.puthuparambil@bell.ca> | 2021-01-27 16:01:33 +0000 |
commit | 099f89eda3b0c94d20612ebf1d62795ab7dc833a (patch) | |
tree | 9ac4eeb64c92bdf8b4de1773dcfd195158b30a6c /cps-service/src/main | |
parent | 34afca00d1f8b2fabe7708c76be714687e136d68 (diff) |
Draft at proposal where the DataNodeBuilder 'replaces' yang Utils to buidl a DataNode
Most complexity is related to immutable collections and the fact taht we are adding data while recursing
over the orignal data in an uncontrolled order.
I cleaned it up as best I could with no logic in DataNode.
Espcially the handling of LitLeaves requires some specialed handling. Thsi is just a draft solution for that
I still propose we get back to that in dedicated user stories for handling the various types of Yang elements
Hope this helps...
Issue-ID: CPS-137
Signed-off-by: ToineSiebelink <toine.siebelink@est.tech>
Change-Id: Iab7cfcff67412c01bcdab95e707e1350bf60fab1
Diffstat (limited to 'cps-service/src/main')
4 files changed, 206 insertions, 258 deletions
diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/Fragment.java b/cps-service/src/main/java/org/onap/cps/api/impl/Fragment.java deleted file mode 100644 index 0f785b718f..0000000000 --- a/cps-service/src/main/java/org/onap/cps/api/impl/Fragment.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2020 Nordix Foundation - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.api.impl; - -import com.google.common.collect.ImmutableList; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import lombok.Getter; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -import org.opendaylight.yangtools.yang.model.api.Module; - -/** - * Class to store a Yang Fragment (container or list element). - */ -public class Fragment { - - @Getter - private final String xpath; - - @Getter - private final Map<String, Object> attributes = new HashMap<>(); - - @Getter - private final Module module; - - @Getter - private final Fragment parentFragment; - - @Getter - private final Set<Fragment> childFragments = new HashSet<>(0); - - private final QName[] qnames; - - private Optional<Set<String>> optionalLeafListNames = Optional.empty(); - - /** - * Create a root Fragment. - * - * @param module the Yang module that encompasses this fragment - * @param qnames the list of qualified names that points the schema node for this fragment - * @param xpath the xpath of root fragment - */ - public static Fragment createRootFragment(final Module module, final QName[] qnames, final String xpath) { - return new Fragment(null, module, qnames, xpath); - } - - /** - * Create a Child Fragment under a given Parent Fragment. - * - * @param parentFragment the parent (can be null for 'root' objects) - * @param module the Yang module that encompasses this fragment - * @param qnames array of qualified names that points the schema node for this fragment - * @param xpath the xpath for this fragment - */ - private Fragment(final Fragment parentFragment, final Module module, final QName[] qnames, final String xpath) { - this.parentFragment = parentFragment; - this.module = module; - this.qnames = qnames; - this.xpath = xpath; - } - - /** - * Create a Child Fragment where the current Fragment is the parent. - * - * @param childQname The Qualified name for the child (relative to the parent) - * @param childXPath The child xpath (relative to the parrent) - * @return the child fragment - */ - public Fragment createChildFragment(final QName childQname, final String childXPath) { - final QName[] qnamesForChild = Arrays.copyOf(qnames, qnames.length + 1); - qnamesForChild[qnamesForChild.length - 1] = childQname; - final Fragment childFragment = new Fragment(this, module, qnamesForChild, getXpath() + childXPath); - childFragments.add(childFragment); - return childFragment; - } - - /** - * Define a leaf list by providing its name. - * The list is not instantiated until the first value is provided - * - * @param name the name of the leaf list - */ - public void addLeafListName(final String name) { - if (optionalLeafListNames.isEmpty()) { - optionalLeafListNames = Optional.of(new HashSet<>()); - } - optionalLeafListNames.get().add(name); - } - - /** - * Add a leaf or leaf list value. - * For Leaf lists it is essential to first define the attribute is a leaf list by using addLeafListName method - * - * @param name the name of the leaf (or leaf list) - * @param value the value of the leaf (or element of leaf list) - */ - public void addLeafValue(final String name, final Object value) { - if (optionalLeafListNames.isPresent() && optionalLeafListNames.get().contains(name)) { - addLeafListValue(name, value); - } else { - attributes.put(name, value); - } - } - - private void addLeafListValue(final String name, final Object value) { - if (attributes.containsKey(name)) { - final ImmutableList<Object> oldList = (ImmutableList<Object>) attributes.get(name); - final List<Object> newList = new ArrayList<>(oldList); - newList.add(value); - attributes.put(name, ImmutableList.copyOf(newList)); - } else { - attributes.put(name, ImmutableList.of(value)); - } - } - - /** - * Get the SchemaNodeIdentifier for this fragment. - * - * @return the SchemaNodeIdentifier - */ - public String getSchemaNodeIdentifier() { - final StringBuilder stringBuilder = new StringBuilder(); - for (final QName qname : qnames) { - stringBuilder.append(qname.getLocalName()); - stringBuilder.append('/'); - } - return stringBuilder.toString(); - } - - /** - * Get the Optional SchemaNode (model) for this data fragment. - * - * @return the Optional SchemaNode - */ - public Optional<DataSchemaNode> getSchemaNode() { - return module.findDataTreeChild(qnames); - } -} diff --git a/cps-service/src/main/java/org/onap/cps/spi/model/DataNode.java b/cps-service/src/main/java/org/onap/cps/spi/model/DataNode.java index e9c6b56ea5..721a7c0426 100644 --- a/cps-service/src/main/java/org/onap/cps/spi/model/DataNode.java +++ b/cps-service/src/main/java/org/onap/cps/spi/model/DataNode.java @@ -1,6 +1,7 @@ /*- * ============LICENSE_START======================================================= * Copyright (C) 2020 Nordix Foundation. All rights reserved. + * Modifications Copyright (C) 2021 Bell Canada. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,24 +22,27 @@ package org.onap.cps.spi.model; import java.util.Collection; +import java.util.Collections; import java.util.Map; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; +import java.util.Optional; +import java.util.Set; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor +@Setter(AccessLevel.PROTECTED) +@Getter public class DataNode { + DataNode() { } + private String dataspace; private String schemaSetName; private String anchorName; private ModuleReference moduleReference; private String xpath; - private Map<String, Object> leaves; + private Map<String, Object> leaves = Collections.emptyMap(); private Collection<String> xpathsChildren; - private Collection<DataNode> childDataNodes; + private Collection<DataNode> childDataNodes = Collections.emptySet(); + private Optional<Set<String>> optionalLeafListNames = Optional.empty(); } diff --git a/cps-service/src/main/java/org/onap/cps/spi/model/DataNodeBuilder.java b/cps-service/src/main/java/org/onap/cps/spi/model/DataNodeBuilder.java new file mode 100644 index 0000000000..cd6a3a2201 --- /dev/null +++ b/cps-service/src/main/java/org/onap/cps/spi/model/DataNodeBuilder.java @@ -0,0 +1,176 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Bell Canada. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.spi.model; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.utils.YangUtils; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.ValueNode; + +@Slf4j +public class DataNodeBuilder { + + private NormalizedNode normalizedNodeTree; + private String xpath; + private Collection<DataNode> childDataNodes = Collections.emptySet(); + + + /** To use {@link NormalizedNode} for creating {@link DataNode}. + * + * @param normalizedNodeTree used for creating the Data Node + * + * @return this {@link DataNodeBuilder} object + */ + public DataNodeBuilder withNormalizedNodeTree(final NormalizedNode normalizedNodeTree) { + this.normalizedNodeTree = normalizedNodeTree; + return this; + } + + /** + * To use xpath for creating {@link DataNode}. + * @param xpath for the data node + * @return DataNodeBuilder + */ + public DataNodeBuilder withXpath(final String xpath) { + this.xpath = xpath; + return this; + } + + /** + * To specify child nodes needs to be used while creating {@link DataNode}. + * @param childDataNodes to be added to the dataNode + * @return DataNodeBuilder + */ + public DataNodeBuilder withChildDataNodes(final Collection<DataNode> childDataNodes) { + // Added as this is being set from test cases . + // Open for suggestions + this.childDataNodes = childDataNodes; + return this; + } + + /** + * To create the {@link DataNode}. + * + * @return {@link DataNode} + */ + public DataNode build() { + if (normalizedNodeTree != null) { + return buildFromNormalizedNodeTree(); + } else { + return buildFromAttributes(); + } + } + + private DataNode buildFromAttributes() { + final DataNode dataNode = new DataNode(); + dataNode.setXpath(xpath); + dataNode.setChildDataNodes(childDataNodes); + return dataNode; + } + + private DataNode buildFromNormalizedNodeTree() { + xpath = YangUtils.buildXpath(normalizedNodeTree.getIdentifier()); + final DataNode dataNode = new DataNodeBuilder().withXpath(xpath).build(); + addDataNodeFromNormalizedNode(dataNode, normalizedNodeTree); + return dataNode; + } + + private void addDataNodeFromNormalizedNode(final DataNode currentDataNode, + final NormalizedNode normalizedNode) { + if (normalizedNode instanceof DataContainerNode) { + addYangContainer(currentDataNode, (DataContainerNode) normalizedNode); + } else if (normalizedNode instanceof MapNode) { + addDataNodeForEachListElement(currentDataNode, (MapNode) normalizedNode); + } else if (normalizedNode instanceof ValueNode) { + final ValueNode valuesNode = (ValueNode) normalizedNode; + addYangLeaf(currentDataNode, valuesNode.getNodeType().getLocalName(), valuesNode.getValue()); + } else if (normalizedNode instanceof LeafSetNode) { + addYangLeafList(currentDataNode, (LeafSetNode) normalizedNode); + } else { + log.warn("Cannot normalize {}", normalizedNode.getClass()); + } + } + + private void addYangContainer(final DataNode currentDataNode, final DataContainerNode dataContainerNode) { + final Collection<NormalizedNode> normalizedChildNodes = dataContainerNode.getValue(); + for (final NormalizedNode normalizedNode : normalizedChildNodes) { + addDataNodeFromNormalizedNode(currentDataNode, normalizedNode); + } + } + + private void addYangLeaf(final DataNode currentDataNode, final String leafName, final Object leafValue) { + final Map leaves = new ImmutableMap.Builder<String, Object>() + .putAll(currentDataNode.getLeaves()) + .put(leafName, leafValue) + .build(); + currentDataNode.setLeaves(leaves); + } + + private void addYangLeafList(final DataNode currentDataNode, final LeafSetNode leafSetNode) { + final ImmutableSet.Builder builder = new ImmutableSet.Builder<String>(); + final String leafListName = leafSetNode.getNodeType().getLocalName(); + final Optional<Set<String>> optionalLeafListNames = currentDataNode.getOptionalLeafListNames(); + if (optionalLeafListNames.isPresent()) { + builder.addAll(optionalLeafListNames.get()); + } + builder.add(leafListName); + final ImmutableSet leafListNames = builder.build(); + currentDataNode.setOptionalLeafListNames(Optional.of(leafListNames)); + final List leafListValues = new LinkedList(); + for (final NormalizedNode normalizedNode : (Collection<NormalizedNode>) leafSetNode.getValue()) { + leafListValues.add(((ValueNode) normalizedNode).getValue()); + } + addYangLeaf(currentDataNode, leafListName, leafListValues); + } + + private void addDataNodeForEachListElement(final DataNode currentDataNode, final MapNode mapNode) { + final Collection<MapEntryNode> mapEntryNodes = mapNode.getValue(); + for (final MapEntryNode mapEntryNode : mapEntryNodes) { + final String xpathChild = YangUtils.buildXpath(mapEntryNode.getIdentifier()); + final DataNode childDataNode = createAndAddChildDataNode(currentDataNode, xpathChild); + addDataNodeFromNormalizedNode(childDataNode, mapEntryNode); + } + } + + private DataNode createAndAddChildDataNode(final DataNode parentDataNode, final String childXpath) { + final DataNode newChildDataNode = new DataNodeBuilder().withXpath(xpath + childXpath) + .build(); + final Set<DataNode> allChildDataNodes = new ImmutableSet.Builder<DataNode>() + .addAll(parentDataNode.getChildDataNodes()) + .add(newChildDataNode) + .build(); + parentDataNode.setChildDataNodes(allChildDataNodes); + return newChildDataNode; + } + +} diff --git a/cps-service/src/main/java/org/onap/cps/utils/YangUtils.java b/cps-service/src/main/java/org/onap/cps/utils/YangUtils.java index 8077ed7d88..1244d54afb 100644 --- a/cps-service/src/main/java/org/onap/cps/utils/YangUtils.java +++ b/cps-service/src/main/java/org/onap/cps/utils/YangUtils.java @@ -1,6 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2020 Nordix Foundation + * Modifications Copyright (C) 2021 Bell Canada. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,35 +23,26 @@ package org.onap.cps.utils; import com.google.gson.stream.JsonReader; import java.io.IOException; import java.io.StringReader; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; -import org.onap.cps.api.impl.Fragment; -import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; -import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; -import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; -import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; -import org.opendaylight.yangtools.yang.data.api.schema.MapNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.ValueNode; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactory; import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactorySupplier; import org.opendaylight.yangtools.yang.data.codec.gson.JsonParserStream; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult; -import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.SchemaContext; @Slf4j public class YangUtils { + private YangUtils() { - throw new IllegalStateException("Utility class"); + // Private constructor fo security reasons } /** @@ -60,7 +52,7 @@ public class YangUtils { * @param schemaContext the SchemaContext for the given data * @return the NormalizedNode representing the json data */ - public static NormalizedNode<?, ?> parseJsonData(final String jsonData, final SchemaContext schemaContext) + public static NormalizedNode parseJsonData(final String jsonData, final SchemaContext schemaContext) throws IOException { final JSONCodecFactory jsonCodecFactory = JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02 .getShared(schemaContext); @@ -76,85 +68,23 @@ public class YangUtils { } /** - * Break a Normalized Node tree into fragments that can be stored by the persistence service. - * - * @param tree the normalized node tree - * @param module the module applicable for the data in the normalized node - * @return the 'root' Fragment for the tree contain all relevant children etc. + * Create an xpath form a Yang Tools NodeIdentifier (i.e. PathArgument). + * @param nodeIdentifier the NodeIdentifier + * @return an xpath */ - public static Fragment fragmentNormalizedNode( - final NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?> tree, - final Module module) { - final QName[] nodeTypes = {tree.getNodeType()}; - final String xpath = buildXpathId(tree.getIdentifier()); - final Fragment rootFragment = Fragment.createRootFragment(module, nodeTypes, xpath); - fragmentNormalizedNode(rootFragment, tree); - return rootFragment; - } - - private static void fragmentNormalizedNode(final Fragment currentFragment, - final NormalizedNode normalizedNode) { - if (normalizedNode instanceof DataContainerNode) { - inspectContainer(currentFragment, (DataContainerNode) normalizedNode); - } else if (normalizedNode instanceof MapNode) { - inspectKeyedList(currentFragment, (MapNode) normalizedNode); - } else if (normalizedNode instanceof ValueNode) { - inspectLeaf(currentFragment, (ValueNode) normalizedNode); - } else if (normalizedNode instanceof LeafSetNode) { - inspectLeafList(currentFragment, (LeafSetNode) normalizedNode); - } else { - log.warn("Cannot normalize {}", normalizedNode.getClass()); - } - } - - private static void inspectLeaf(final Fragment currentFragment, - final ValueNode valueNode) { - final Object value = valueNode.getValue(); - currentFragment.addLeafValue(valueNode.getNodeType().getLocalName(), value); - } - - private static void inspectLeafList(final Fragment currentFragment, - final LeafSetNode leafSetNode) { - currentFragment.addLeafListName(leafSetNode.getNodeType().getLocalName()); - for (final NormalizedNode value : (Collection<NormalizedNode>) leafSetNode.getValue()) { - fragmentNormalizedNode(currentFragment, value); - } - } - - private static void inspectContainer(final Fragment currentFragment, - final DataContainerNode dataContainerNode) { - final Collection<NormalizedNode> leaves = (Collection) dataContainerNode.getValue(); - for (final NormalizedNode leaf : leaves) { - fragmentNormalizedNode(currentFragment, leaf); - } - } - - private static void inspectKeyedList(final Fragment currentFragment, - final MapNode mapNode) { - createNodeForEachListElement(currentFragment, mapNode); - } - - private static void createNodeForEachListElement(final Fragment currentFragment, final MapNode mapNode) { - final Collection<MapEntryNode> mapEntryNodes = mapNode.getValue(); - for (final MapEntryNode mapEntryNode : mapEntryNodes) { - final String xpathId = buildXpathId(mapEntryNode.getIdentifier()); - final Fragment listElementFragment = - currentFragment.createChildFragment(mapNode.getNodeType(), xpathId); - fragmentNormalizedNode(listElementFragment, mapEntryNode); - } - } - - private static String buildXpathId(final YangInstanceIdentifier.PathArgument nodeIdentifier) { - final StringBuilder xpathIdBuilder = new StringBuilder(); - xpathIdBuilder.append("/").append(nodeIdentifier.getNodeType().getLocalName()); + public static String buildXpath(final YangInstanceIdentifier.PathArgument nodeIdentifier) { + final StringBuilder xpathBuilder = new StringBuilder(); + xpathBuilder.append("/").append(nodeIdentifier.getNodeType().getLocalName()); - if (nodeIdentifier instanceof NodeIdentifierWithPredicates) { - xpathIdBuilder.append(getKeyAttributesStatement((NodeIdentifierWithPredicates) nodeIdentifier)); + if (nodeIdentifier instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates) { + xpathBuilder.append(getKeyAttributesStatement( + (YangInstanceIdentifier.NodeIdentifierWithPredicates) nodeIdentifier)); } - return xpathIdBuilder.toString(); + return xpathBuilder.toString(); } - private static String getKeyAttributesStatement(final NodeIdentifierWithPredicates nodeIdentifier) { + private static String getKeyAttributesStatement( + final YangInstanceIdentifier.NodeIdentifierWithPredicates nodeIdentifier) { final List<String> keyAttributes = nodeIdentifier.entrySet().stream().map( entry -> { final String name = entry.getKey().getLocalName(); |