Simplify trimming
[netconf.git] / restconf / restconf-nb-rfc8040 / src / main / java / org / opendaylight / restconf / nb / rfc8040 / rests / utils / ReadDataTransactionUtil.java
index 2aa4f0603b815b74c8e59190cffa11398fb466fe..76f0d6f1a27f72bdc0767b512a68045977cd0667 100644 (file)
@@ -7,38 +7,24 @@
  */
 package org.opendaylight.restconf.nb.rfc8040.rests.utils;
 
-import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.STREAMS_PATH;
-import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.STREAM_PATH_PART;
-
-import com.google.common.base.Optional;
-import com.google.common.collect.Iterables;
-import com.google.common.primitives.Ints;
-import com.google.common.util.concurrent.CheckedFuture;
-import java.net.URI;
+import com.google.common.util.concurrent.ListenableFuture;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.function.Function;
 import java.util.stream.Collectors;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import javax.ws.rs.core.UriInfo;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
-import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
-import org.opendaylight.restconf.common.context.WriterParameters;
-import org.opendaylight.restconf.common.context.WriterParameters.WriterParametersBuilder;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError;
-import org.opendaylight.restconf.nb.rfc8040.references.SchemaContextRef;
-import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
-import org.opendaylight.restconf.nb.rfc8040.streams.listeners.NotificationListenerAdapter;
-import org.opendaylight.restconf.nb.rfc8040.utils.mapping.RestconfMappingNodeUtil;
-import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserFieldsParameter;
-import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
+import org.opendaylight.restconf.nb.rfc8040.ContentParam;
+import org.opendaylight.restconf.nb.rfc8040.WithDefaultsParam;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
+import org.opendaylight.yangtools.yang.common.ErrorTag;
+import org.opendaylight.yangtools.yang.common.ErrorType;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -51,24 +37,32 @@ import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+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.SystemLeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.SystemMapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UserLeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UserMapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.builder.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.api.schema.builder.ListNodeBuilder;
+import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeBuilder;
+import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeContainerBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.SchemaAwareBuilders;
 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 /**
  * Util class for read data from data store via transaction.
@@ -77,251 +71,102 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
  * <li>state
  * <li>all (config + state)
  * </ul>
- *
  */
 public final class ReadDataTransactionUtil {
-
     private ReadDataTransactionUtil() {
-        throw new UnsupportedOperationException("Util class.");
-    }
-
-    /**
-     * Parse parameters from URI request and check their types and values.
-     *
-     *
-     * @param identifier
-     *             {@link InstanceIdentifierContext}
-     * @param uriInfo
-     *             URI info
-     * @param tagged
-     *             set tagged for {@link WriterParameters}
-     * @return {@link WriterParameters}
-     */
-    @Nonnull
-    public static WriterParameters parseUriParameters(@Nonnull final InstanceIdentifierContext<?> identifier,
-            @Nullable final UriInfo uriInfo, final boolean tagged) {
-        return parseParams(identifier, uriInfo, tagged);
-    }
-
-    /**
-     * Parse parameters from URI request and check their types and values.
-     *
-     *
-     * @param identifier
-     *             {@link InstanceIdentifierContext}
-     * @param uriInfo
-     *             URI info
-     * @return {@link WriterParameters}
-     */
-    @Nonnull
-    public static WriterParameters parseUriParameters(@Nonnull final InstanceIdentifierContext<?> identifier,
-                                                               @Nullable final UriInfo uriInfo) {
-        return parseParams(identifier, uriInfo, false);
-    }
-
-    private static WriterParameters parseParams(final InstanceIdentifierContext<?> identifier, final UriInfo uriInfo,
-            final boolean tagged) {
-        final WriterParametersBuilder builder = new WriterParametersBuilder();
-        builder.setTagged(tagged);
-
-        if (uriInfo == null) {
-            return builder.build();
-        }
-
-        // check only allowed parameters
-        ParametersUtil.checkParametersTypes(
-                RestconfDataServiceConstant.ReadData.READ_TYPE_TX,
-                uriInfo.getQueryParameters().keySet(),
-                RestconfDataServiceConstant.ReadData.CONTENT,
-                RestconfDataServiceConstant.ReadData.DEPTH,
-                RestconfDataServiceConstant.ReadData.FIELDS, RestconfDataServiceConstant.ReadData.WITH_DEFAULTS);
-
-        // read parameters from URI or set default values
-        final List<String> content = uriInfo.getQueryParameters().getOrDefault(
-                RestconfDataServiceConstant.ReadData.CONTENT,
-                Collections.singletonList(RestconfDataServiceConstant.ReadData.ALL));
-        final List<String> depth = uriInfo.getQueryParameters().getOrDefault(
-                RestconfDataServiceConstant.ReadData.DEPTH,
-                Collections.singletonList(RestconfDataServiceConstant.ReadData.UNBOUNDED));
-        // fields
-        final List<String> fields = uriInfo.getQueryParameters().getOrDefault(
-                RestconfDataServiceConstant.ReadData.FIELDS,
-                Collections.emptyList());
-
-        // parameter can be in URI at most once
-        ParametersUtil.checkParameterCount(content, RestconfDataServiceConstant.ReadData.CONTENT);
-        ParametersUtil.checkParameterCount(depth, RestconfDataServiceConstant.ReadData.DEPTH);
-        ParametersUtil.checkParameterCount(fields, RestconfDataServiceConstant.ReadData.FIELDS);
-
-        // check and set content
-        final String contentValue = content.get(0);
-        if (!contentValue.equals(RestconfDataServiceConstant.ReadData.ALL)) {
-            if (!contentValue.equals(RestconfDataServiceConstant.ReadData.CONFIG)
-                    && !contentValue.equals(RestconfDataServiceConstant.ReadData.NONCONFIG)) {
-                throw new RestconfDocumentedException(
-                        new RestconfError(RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.INVALID_VALUE,
-                                "Invalid content parameter: " + contentValue, null,
-                                "The content parameter value must be either config, nonconfig or all (default)"));
-            }
-        }
-
-        builder.setContent(content.get(0));
-
-        // check and set depth
-        if (!depth.get(0).equals(RestconfDataServiceConstant.ReadData.UNBOUNDED)) {
-            final Integer value = Ints.tryParse(depth.get(0));
-
-            if ((value == null)
-                    || (!((value >= RestconfDataServiceConstant.ReadData.MIN_DEPTH)
-                        && (value <= RestconfDataServiceConstant.ReadData.MAX_DEPTH)))) {
-                throw new RestconfDocumentedException(
-                        new RestconfError(RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.INVALID_VALUE,
-                                "Invalid depth parameter: " + depth, null,
-                                "The depth parameter must be an integer between 1 and 65535 or \"unbounded\""));
-            } else {
-                builder.setDepth(value);
-            }
-        }
-
-        // check and set fields
-        if (!fields.isEmpty()) {
-            builder.setFields(ParserFieldsParameter.parseFieldsParameter(identifier, fields.get(0)));
-        }
-
-        return builder.build();
-    }
-
-    /**
-     * Read specific type of data from data store via transaction.
-     *
-     * @param valueOfContent
-     *            type of data to read (config, state, all)
-     * @param transactionNode
-     *            {@link TransactionVarsWrapper} - wrapper for variables
-     * @param schemaContext
-     *            schema context
-     * @return {@link NormalizedNode}
-     */
-    @Nullable
-    public static NormalizedNode<?, ?> readData(@Nonnull final String valueOfContent,
-            @Nonnull final TransactionVarsWrapper transactionNode, final SchemaContext schemaContext) {
-        return readData(valueOfContent, transactionNode, null, schemaContext);
+        // Hidden on purpose
     }
 
     /**
-     * Read specific type of data from data store via transaction.
+     * Read specific type of data from data store via transaction. Close {@link DOMTransactionChain} if any
+     * inside of object {@link RestconfStrategy} provided as a parameter.
      *
-     * @param valueOfContent
-     *            type of data to read (config, state, all)
-     * @param transactionNode
-     *            {@link TransactionVarsWrapper} - wrapper for variables
-     * @param withDefa
-     *            vaule of with-defaults parameter
-     * @param ctx
-     *            schema context
+     * @param content        type of data to read (config, state, all)
+     * @param path           the path to read
+     * @param strategy       {@link RestconfStrategy} - object that perform the actual DS operations
+     * @param withDefa       value of with-defaults parameter
+     * @param ctx            schema context
      * @return {@link NormalizedNode}
      */
-    @Nullable
-    public static NormalizedNode<?, ?> readData(@Nonnull final String valueOfContent,
-            @Nonnull final TransactionVarsWrapper transactionNode, final String withDefa, final SchemaContext ctx) {
-        switch (valueOfContent) {
-            case RestconfDataServiceConstant.ReadData.CONFIG:
-                transactionNode.setLogicalDatastoreType(LogicalDatastoreType.CONFIGURATION);
-                if (withDefa == null) {
-                    return readDataViaTransaction(transactionNode);
-                } else {
-                    return prepareDataByParamWithDef(readDataViaTransaction(transactionNode),
-                            transactionNode.getInstanceIdentifier().getInstanceIdentifier(), withDefa, ctx);
-                }
-            case RestconfDataServiceConstant.ReadData.NONCONFIG:
-                transactionNode.setLogicalDatastoreType(LogicalDatastoreType.OPERATIONAL);
-                return readDataViaTransaction(transactionNode);
-
-            case RestconfDataServiceConstant.ReadData.ALL:
-                return readAllData(transactionNode, withDefa, ctx);
-
+    public static @Nullable NormalizedNode readData(final @NonNull ContentParam content,
+                                                    final @NonNull YangInstanceIdentifier path,
+                                                    final @NonNull RestconfStrategy strategy,
+                                                    final WithDefaultsParam withDefa,
+                                                    final EffectiveModelContext ctx) {
+        // FIXME: use a switch expression when they are available, removing source of RestconfDocumentedException
+        switch (content) {
+            case ALL:
+                return readAllData(strategy, path, withDefa, ctx);
+            case CONFIG:
+                final NormalizedNode read = readDataViaTransaction(strategy, LogicalDatastoreType.CONFIGURATION, path);
+                return withDefa == null ? read : prepareDataByParamWithDef(read, path, withDefa, ctx);
+            case NONCONFIG:
+                return readDataViaTransaction(strategy, LogicalDatastoreType.OPERATIONAL, path);
             default:
                 throw new RestconfDocumentedException(
-                        new RestconfError(RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.INVALID_VALUE,
-                                "Invalid content parameter: " + valueOfContent, null,
+                        new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
+                                "Invalid content parameter: " + content.paramValue(), null,
                                 "The content parameter value must be either config, nonconfig or all (default)"));
         }
     }
 
     /**
-     * Read specific type of data from data store via transaction and if identifier read data from
-     * streams then put streams from actual schema context to datastore.
+     * Read specific type of data from data store via transaction with specified subtrees that should only be read.
+     * Close {@link DOMTransactionChain} inside of object {@link RestconfStrategy} provided as a parameter.
      *
-     * @param identifier
-     *             identifier of data to read
-     * @param content
-     *             type of data to read (config, state, all)
-     * @param transactionNode
-     *             {@link TransactionVarsWrapper} - wrapper for variables
-     * @param withDefa
-     *             vaule of with-defaults parameter
-     * @param schemaContextRef
-     *             schema context
-     * @param uriInfo
-     *             uri info
+     * @param content        type of data to read (config, state, all)
+     * @param path           the parent path to read
+     * @param strategy       {@link RestconfStrategy} - object that perform the actual DS operations
+     * @param withDefa       value of with-defaults parameter
+     * @param ctx            schema context
+     * @param fields         paths to selected subtrees which should be read, relative to to the parent path
      * @return {@link NormalizedNode}
      */
-    public static NormalizedNode<?, ?> readData(final String identifier, final String content,
-                                                final TransactionVarsWrapper transactionNode, final String withDefa,
-                                                final SchemaContextRef schemaContextRef, final UriInfo uriInfo) {
-        final SchemaContext schemaContext = schemaContextRef.get();
-        if (identifier.contains(STREAMS_PATH) && !identifier.contains(STREAM_PATH_PART)) {
-            final DOMDataReadWriteTransaction wTx = transactionNode.getTransactionChain().newReadWriteTransaction();
-            final boolean exist = SubscribeToStreamUtil.checkExist(schemaContext, wTx);
-
-            for (final NotificationDefinition notificationDefinition : schemaContextRef.get().getNotifications()) {
-                final List<NotificationListenerAdapter> notifiStreamXML =
-                        CreateStreamUtil.createYangNotifiStream(notificationDefinition, schemaContextRef,
-                                NotificationOutputType.XML.getName());
-                final List<NotificationListenerAdapter> notifiStreamJSON =
-                        CreateStreamUtil.createYangNotifiStream(notificationDefinition, schemaContextRef,
-                                NotificationOutputType.JSON.getName());
-                for (final NotificationListenerAdapter listener : Iterables.concat(notifiStreamXML, notifiStreamJSON)) {
-                    final URI uri = SubscribeToStreamUtil.prepareUriByStreamName(uriInfo, listener.getStreamName());
-                    final NormalizedNode mapToStreams =
-                            RestconfMappingNodeUtil.mapYangNotificationStreamByIetfRestconfMonitoring(
-                                    listener.getSchemaPath().getLastComponent(), schemaContext.getNotifications(),
-                                    null, listener.getOutputType(), uri,
-                                    SubscribeToStreamUtil.getMonitoringModule(schemaContext), exist);
-                    SubscribeToStreamUtil.writeDataToDS(schemaContext,
-                            listener.getSchemaPath().getLastComponent().getLocalName(), wTx, exist,
-                            mapToStreams);
-                }
-            }
-            SubscribeToStreamUtil.submitData(wTx);
+    public static @Nullable NormalizedNode readData(final @NonNull ContentParam content,
+            final @NonNull YangInstanceIdentifier path, final @NonNull RestconfStrategy strategy,
+            final @Nullable WithDefaultsParam withDefa, @NonNull final EffectiveModelContext ctx,
+            final @NonNull List<YangInstanceIdentifier> fields) {
+        // FIXME: use a switch expression when they are available, removing source of RestconfDocumentedException
+        switch (content) {
+            case ALL:
+                return readAllData(strategy, path, withDefa, ctx, fields);
+            case CONFIG:
+                final NormalizedNode read = readDataViaTransaction(strategy, LogicalDatastoreType.CONFIGURATION, path,
+                    fields);
+                return withDefa == null ? read : prepareDataByParamWithDef(read, path, withDefa, ctx);
+            case NONCONFIG:
+                return readDataViaTransaction(strategy, LogicalDatastoreType.OPERATIONAL, path, fields);
+            default:
+                throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
+                        "Invalid content parameter: " + content.paramValue(), null,
+                        "The content parameter value must be either config, nonconfig or all (default)"));
         }
-        return readData(content, transactionNode, withDefa, schemaContext);
     }
 
-    private static NormalizedNode<?, ?> prepareDataByParamWithDef(final NormalizedNode<?, ?> result,
-            final YangInstanceIdentifier path, final String withDefa, final SchemaContext ctx) {
+    private static NormalizedNode prepareDataByParamWithDef(final NormalizedNode result,
+            final YangInstanceIdentifier path, final WithDefaultsParam withDefa, final EffectiveModelContext ctx) {
         boolean trim;
         switch (withDefa) {
-            case "trim":
+            case TRIM:
                 trim = true;
                 break;
-            case "explicit":
+            case EXPLICIT:
                 trim = false;
                 break;
             default:
-                throw new RestconfDocumentedException("");
+                throw new RestconfDocumentedException("Unsupported with-defaults value " + withDefa.paramValue());
         }
 
         final DataSchemaContextTree baseSchemaCtxTree = DataSchemaContextTree.from(ctx);
-        final DataSchemaNode baseSchemaNode = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
+        final DataSchemaNode baseSchemaNode = baseSchemaCtxTree.findChild(path).orElseThrow().getDataSchemaNode();
         if (result instanceof ContainerNode) {
-            final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder =
-                    Builders.containerBuilder((ContainerSchemaNode) baseSchemaNode);
+            final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder =
+                SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) baseSchemaNode);
             buildCont(builder, (ContainerNode) result, baseSchemaCtxTree, path, trim);
             return builder.build();
         } else {
-            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
-                    Builders.mapEntryBuilder((ListSchemaNode) baseSchemaNode);
+            final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
+                SchemaAwareBuilders.mapEntryBuilder((ListSchemaNode) baseSchemaNode);
             buildMapEntryBuilder(builder, (MapEntryNode) result, baseSchemaCtxTree, path, trim,
                     ((ListSchemaNode) baseSchemaNode).getKeyDefinition());
             return builder.build();
@@ -329,151 +174,182 @@ public final class ReadDataTransactionUtil {
     }
 
     private static void buildMapEntryBuilder(
-            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder,
+            final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder,
             final MapEntryNode result, final DataSchemaContextTree baseSchemaCtxTree,
             final YangInstanceIdentifier actualPath, final boolean trim, final List<QName> keys) {
-        for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
+        for (final DataContainerChild child : result.body()) {
             final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
-            final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
+            final DataSchemaNode childSchema = baseSchemaCtxTree.findChild(path).orElseThrow().getDataSchemaNode();
             if (child instanceof ContainerNode) {
-                final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> childBuilder =
-                        Builders.containerBuilder((ContainerSchemaNode) childSchema);
+                final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> childBuilder =
+                    SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) childSchema);
                 buildCont(childBuilder, (ContainerNode) child, baseSchemaCtxTree, path, trim);
                 builder.withChild(childBuilder.build());
             } else if (child instanceof MapNode) {
-                final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
-                        Builders.mapBuilder((ListSchemaNode) childSchema);
+                final CollectionNodeBuilder<MapEntryNode, SystemMapNode> childBuilder =
+                    SchemaAwareBuilders.mapBuilder((ListSchemaNode) childSchema);
                 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
                         ((ListSchemaNode) childSchema).getKeyDefinition());
                 builder.withChild(childBuilder.build());
             } else if (child instanceof LeafNode) {
-                final String defaultVal = ((LeafSchemaNode) childSchema).getDefault();
-                final String nodeVal = ((LeafNode<String>) child).getValue();
-                final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
-                        Builders.leafBuilder((LeafSchemaNode) childSchema);
-                if (keys.contains(child.getNodeType())) {
-                    leafBuilder.withValue(((LeafNode) child).getValue());
+                final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
+                final Object nodeVal = child.body();
+                final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
+                    SchemaAwareBuilders.leafBuilder((LeafSchemaNode) childSchema);
+                if (keys.contains(child.getIdentifier().getNodeType())) {
+                    leafBuilder.withValue(((LeafNode<?>) child).body());
                     builder.withChild(leafBuilder.build());
-                } else {
-                    if (trim) {
-                        if ((defaultVal == null) || !defaultVal.equals(nodeVal)) {
-                            leafBuilder.withValue(((LeafNode) child).getValue());
-                            builder.withChild(leafBuilder.build());
-                        }
-                    } else {
-                        if ((defaultVal != null) && defaultVal.equals(nodeVal)) {
-                            leafBuilder.withValue(((LeafNode) child).getValue());
-                            builder.withChild(leafBuilder.build());
-                        }
+                } else if (trim) {
+                    if (defaultVal == null || !defaultVal.equals(nodeVal)) {
+                        leafBuilder.withValue(((LeafNode<?>) child).body());
+                        builder.withChild(leafBuilder.build());
                     }
+                } else if (defaultVal != null && defaultVal.equals(nodeVal)) {
+                    leafBuilder.withValue(((LeafNode<?>) child).body());
+                    builder.withChild(leafBuilder.build());
                 }
             }
         }
     }
 
-    private static void buildList(final CollectionNodeBuilder<MapEntryNode, MapNode> builder, final MapNode result,
-            final DataSchemaContextTree baseSchemaCtxTree, final YangInstanceIdentifier path, final boolean trim,
-            final List<QName> keys) {
-        for (final MapEntryNode mapEntryNode : result.getValue()) {
+    private static void buildList(final CollectionNodeBuilder<MapEntryNode, SystemMapNode> builder,
+            final MapNode result, final DataSchemaContextTree baseSchemaCtxTree, final YangInstanceIdentifier path,
+            final boolean trim, final List<QName> keys) {
+        for (final MapEntryNode mapEntryNode : result.body()) {
             final YangInstanceIdentifier actualNode = path.node(mapEntryNode.getIdentifier());
-            final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(actualNode).getDataSchemaNode();
-            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
-                    Builders.mapEntryBuilder((ListSchemaNode) childSchema);
+            final DataSchemaNode childSchema = baseSchemaCtxTree.findChild(actualNode).orElseThrow()
+                    .getDataSchemaNode();
+            final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
+                SchemaAwareBuilders.mapEntryBuilder((ListSchemaNode) childSchema);
             buildMapEntryBuilder(mapEntryBuilder, mapEntryNode, baseSchemaCtxTree, actualNode, trim, keys);
             builder.withChild(mapEntryBuilder.build());
         }
     }
 
-    private static void buildCont(final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder,
+    private static void buildCont(final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder,
             final ContainerNode result, final DataSchemaContextTree baseSchemaCtxTree,
             final YangInstanceIdentifier actualPath, final boolean trim) {
-        for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
+        for (final DataContainerChild child : result.body()) {
             final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
-            final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
+            final DataSchemaNode childSchema = baseSchemaCtxTree.findChild(path).orElseThrow().getDataSchemaNode();
             if (child instanceof ContainerNode) {
-                final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builderChild =
-                        Builders.containerBuilder((ContainerSchemaNode) childSchema);
+                final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builderChild =
+                    SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) childSchema);
                 buildCont(builderChild, result, baseSchemaCtxTree, actualPath, trim);
                 builder.withChild(builderChild.build());
             } else if (child instanceof MapNode) {
-                final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
-                        Builders.mapBuilder((ListSchemaNode) childSchema);
+                final CollectionNodeBuilder<MapEntryNode, SystemMapNode> childBuilder =
+                    SchemaAwareBuilders.mapBuilder((ListSchemaNode) childSchema);
                 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
                         ((ListSchemaNode) childSchema).getKeyDefinition());
                 builder.withChild(childBuilder.build());
             } else if (child instanceof LeafNode) {
-                final String defaultVal = ((LeafSchemaNode) childSchema).getDefault();
-                final String nodeVal = ((LeafNode<String>) child).getValue();
-                final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
-                        Builders.leafBuilder((LeafSchemaNode) childSchema);
+                final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
+                final Object nodeVal = child.body();
+                final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
+                    SchemaAwareBuilders.leafBuilder((LeafSchemaNode) childSchema);
                 if (trim) {
-                    if ((defaultVal == null) || !defaultVal.equals(nodeVal)) {
-                        leafBuilder.withValue(((LeafNode) child).getValue());
-                        builder.withChild(leafBuilder.build());
-                    }
-                } else {
-                    if ((defaultVal != null) && defaultVal.equals(nodeVal)) {
-                        leafBuilder.withValue(((LeafNode) child).getValue());
+                    if (defaultVal == null || !defaultVal.equals(nodeVal)) {
+                        leafBuilder.withValue(((LeafNode<?>) child).body());
                         builder.withChild(leafBuilder.build());
                     }
+                } else if (defaultVal != null && defaultVal.equals(nodeVal)) {
+                    leafBuilder.withValue(((LeafNode<?>) child).body());
+                    builder.withChild(leafBuilder.build());
                 }
             }
         }
     }
 
     /**
-     * If is set specific {@link LogicalDatastoreType} in
-     * {@link TransactionVarsWrapper}, then read this type of data from DS. If
-     * don't, we have to read all data from DS (state + config)
+     * If is set specific {@link LogicalDatastoreType} in {@link RestconfStrategy}, then read this type of data from DS.
+     * If don't, we have to read all data from DS (state + config)
      *
-     * @param transactionNode
-     *             {@link TransactionVarsWrapper} - wrapper for variables
+     * @param strategy              {@link RestconfStrategy} - object that perform the actual DS operations
+     * @param closeTransactionChain If is set to true, after transaction it will close transactionChain
+     *                              in {@link RestconfStrategy} if any
      * @return {@link NormalizedNode}
      */
-    @Nullable
-    private static NormalizedNode<?, ?> readDataViaTransaction(
-            @Nonnull final TransactionVarsWrapper transactionNode) {
+    static @Nullable NormalizedNode readDataViaTransaction(final @NonNull RestconfStrategy strategy,
+            final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+        return extractReadData(strategy, path, strategy.read(store, path));
+    }
+
+    /**
+     * Read specific type of data {@link LogicalDatastoreType} via transaction in {@link RestconfStrategy} with
+     * specified subtrees that should only be read.
+     *
+     * @param strategy              {@link RestconfStrategy} - object that perform the actual DS operations
+     * @param store                 datastore type
+     * @param path                  parent path to selected fields
+     * @param closeTransactionChain if it is set to {@code true}, after transaction it will close transactionChain
+     *                              in {@link RestconfStrategy} if any
+     * @param fields                paths to selected subtrees which should be read, relative to to the parent path
+     * @return {@link NormalizedNode}
+     */
+    private static @Nullable NormalizedNode readDataViaTransaction(final @NonNull RestconfStrategy strategy,
+            final @NonNull LogicalDatastoreType store, final @NonNull YangInstanceIdentifier path,
+            final @NonNull List<YangInstanceIdentifier> fields) {
+        return extractReadData(strategy, path, strategy.read(store, path, fields));
+    }
+
+    private static NormalizedNode extractReadData(final RestconfStrategy strategy,
+            final YangInstanceIdentifier path, final ListenableFuture<Optional<NormalizedNode>> dataFuture) {
         final NormalizedNodeFactory dataFactory = new NormalizedNodeFactory();
-        try (DOMDataReadOnlyTransaction tx = transactionNode.getTransactionChain().newReadOnlyTransaction()) {
-            final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> listenableFuture = tx.read(
-                transactionNode.getLogicalDatastoreType(),
-                transactionNode.getInstanceIdentifier().getInstanceIdentifier());
-            FutureCallbackTx.addCallback(listenableFuture, RestconfDataServiceConstant.ReadData.READ_TYPE_TX,
-                dataFactory);
-        }
+        FutureCallbackTx.addCallback(dataFuture, "READ", dataFactory, path);
         return dataFactory.build();
     }
 
     /**
-     * Read config and state data, then map them.
+     * Read config and state data, then map them. Close {@link DOMTransactionChain} inside of object
+     * {@link RestconfStrategy} provided as a parameter if any.
      *
-     * @param transactionNode
-     *            {@link TransactionVarsWrapper} - wrapper for variables
-     * @param withDefa
-     *            with-defaults parameter
-     * @param ctx
-     *            schema context
+     * @param strategy {@link RestconfStrategy} - object that perform the actual DS operations
+     * @param withDefa with-defaults parameter
+     * @param ctx      schema context
      * @return {@link NormalizedNode}
      */
-    @Nullable
-    private static NormalizedNode<?, ?> readAllData(@Nonnull final TransactionVarsWrapper transactionNode,
-            final String withDefa, final SchemaContext ctx) {
+    private static @Nullable NormalizedNode readAllData(final @NonNull RestconfStrategy strategy,
+            final YangInstanceIdentifier path, final WithDefaultsParam withDefa, final EffectiveModelContext ctx) {
         // PREPARE STATE DATA NODE
-        transactionNode.setLogicalDatastoreType(LogicalDatastoreType.OPERATIONAL);
-        final NormalizedNode<?, ?> stateDataNode = readDataViaTransaction(transactionNode);
+        final NormalizedNode stateDataNode = readDataViaTransaction(strategy, LogicalDatastoreType.OPERATIONAL, path);
+        // PREPARE CONFIG DATA NODE
+        final NormalizedNode configDataNode = readDataViaTransaction(strategy, LogicalDatastoreType.CONFIGURATION,
+            path);
+
+        return mergeConfigAndSTateDataIfNeeded(stateDataNode,
+            withDefa == null ? configDataNode : prepareDataByParamWithDef(configDataNode, path, withDefa, ctx));
+    }
+
+    /**
+     * Read config and state data with selected subtrees that should only be read, then map them.
+     * Close {@link DOMTransactionChain} inside of object {@link RestconfStrategy} provided as a parameter.
+     *
+     * @param strategy {@link RestconfStrategy} - object that perform the actual DS operations
+     * @param path     parent path to selected fields
+     * @param withDefa with-defaults parameter
+     * @param ctx      schema context
+     * @param fields   paths to selected subtrees which should be read, relative to to the parent path
+     * @return {@link NormalizedNode}
+     */
+    private static @Nullable NormalizedNode readAllData(final @NonNull RestconfStrategy strategy,
+            final @NonNull YangInstanceIdentifier path, final @Nullable WithDefaultsParam withDefa,
+            final @NonNull EffectiveModelContext ctx, final @NonNull List<YangInstanceIdentifier> fields) {
+        // PREPARE STATE DATA NODE
+        final NormalizedNode stateDataNode = readDataViaTransaction(strategy, LogicalDatastoreType.OPERATIONAL, path,
+            fields);
 
         // PREPARE CONFIG DATA NODE
-        transactionNode.setLogicalDatastoreType(LogicalDatastoreType.CONFIGURATION);
-        final NormalizedNode<?, ?> configDataNode;
-        if (withDefa == null) {
-            configDataNode = readDataViaTransaction(transactionNode);
-        } else {
-            configDataNode = prepareDataByParamWithDef(readDataViaTransaction(transactionNode),
-                    transactionNode.getInstanceIdentifier().getInstanceIdentifier(), withDefa, ctx);
-        }
+        final NormalizedNode configDataNode = readDataViaTransaction(strategy, LogicalDatastoreType.CONFIGURATION, path,
+            fields);
+        return mergeConfigAndSTateDataIfNeeded(stateDataNode,
+            withDefa == null ? configDataNode : prepareDataByParamWithDef(configDataNode, path, withDefa, ctx));
+    }
 
+    private static NormalizedNode mergeConfigAndSTateDataIfNeeded(final NormalizedNode stateDataNode,
+                                                                  final NormalizedNode configDataNode) {
         // if no data exists
-        if ((stateDataNode == null) && (configDataNode == null)) {
+        if (stateDataNode == null && configDataNode == null) {
             return null;
         }
 
@@ -488,22 +364,19 @@ public final class ReadDataTransactionUtil {
         }
 
         // merge data from config and state
-        return mapNode(stateDataNode, configDataNode);
+        return mergeStateAndConfigData(stateDataNode, configDataNode);
     }
 
     /**
-     * Map data by type of read node.
+     * Merge state and config data into a single NormalizedNode.
      *
-     * @param stateDataNode
-     *             data node of state data
-     * @param configDataNode
-     *             data node of config data
+     * @param stateDataNode  data node of state data
+     * @param configDataNode data node of config data
      * @return {@link NormalizedNode}
      */
-    @Nonnull
-    private static NormalizedNode<?, ?> mapNode(@Nonnull final NormalizedNode<?, ?> stateDataNode,
-                                                         @Nonnull final NormalizedNode<?, ?> configDataNode) {
-        validPossibilityOfMergeNodes(stateDataNode, configDataNode);
+    private static @NonNull NormalizedNode mergeStateAndConfigData(
+            final @NonNull NormalizedNode stateDataNode, final @NonNull NormalizedNode configDataNode) {
+        validateNodeMerge(stateDataNode, configDataNode);
         if (configDataNode instanceof RpcDefinition) {
             return prepareRpcData(configDataNode, stateDataNode);
         } else {
@@ -512,34 +385,29 @@ public final class ReadDataTransactionUtil {
     }
 
     /**
-     * Valid of can be data merged together.
+     * Validates whether the two NormalizedNodes can be merged.
      *
-     * @param stateDataNode
-     *             data node of state data
-     * @param configDataNode
-     *             data node of config data
+     * @param stateDataNode  data node of state data
+     * @param configDataNode data node of config data
      */
-    private static void validPossibilityOfMergeNodes(@Nonnull final NormalizedNode<?, ?> stateDataNode,
-                                                     @Nonnull final NormalizedNode<?, ?> configDataNode) {
+    private static void validateNodeMerge(final @NonNull NormalizedNode stateDataNode,
+                                          final @NonNull NormalizedNode configDataNode) {
         final QNameModule moduleOfStateData = stateDataNode.getIdentifier().getNodeType().getModule();
         final QNameModule moduleOfConfigData = configDataNode.getIdentifier().getNodeType().getModule();
-        if (moduleOfStateData != moduleOfConfigData) {
-            throw new RestconfDocumentedException("It is not possible to merge ");
+        if (!moduleOfStateData.equals(moduleOfConfigData)) {
+            throw new RestconfDocumentedException("Unable to merge data from different modules.");
         }
     }
 
     /**
      * Prepare and map data for rpc.
      *
-     * @param configDataNode
-     *             data node of config data
-     * @param stateDataNode
-     *             data node of state data
+     * @param configDataNode data node of config data
+     * @param stateDataNode  data node of state data
      * @return {@link NormalizedNode}
      */
-    @Nonnull
-    private static NormalizedNode<?, ?> prepareRpcData(@Nonnull final NormalizedNode<?, ?> configDataNode,
-                                                                @Nonnull final NormalizedNode<?, ?> stateDataNode) {
+    private static @NonNull NormalizedNode prepareRpcData(final @NonNull NormalizedNode configDataNode,
+                                                          final @NonNull NormalizedNode stateDataNode) {
         final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder = ImmutableNodes
                 .mapEntryBuilder();
         mapEntryBuilder.withNodeIdentifier((NodeIdentifierWithPredicates) configDataNode.getIdentifier());
@@ -549,41 +417,46 @@ public final class ReadDataTransactionUtil {
         // MAP STATE DATA
         mapRpcDataNode(stateDataNode, mapEntryBuilder);
 
-        return ImmutableNodes.mapNodeBuilder(configDataNode.getNodeType()).addChild(mapEntryBuilder.build()).build();
+        return ImmutableNodes.mapNodeBuilder(configDataNode.getIdentifier().getNodeType())
+            .addChild(mapEntryBuilder.build())
+            .build();
     }
 
     /**
      * Map node to map entry builder.
      *
-     * @param dataNode
-     *             data node
-     * @param mapEntryBuilder
-     *             builder for mapping data
+     * @param dataNode        data node
+     * @param mapEntryBuilder builder for mapping data
      */
-    private static void mapRpcDataNode(@Nonnull final NormalizedNode<?, ?> dataNode,
-                                       @Nonnull final DataContainerNodeBuilder<
-                                               NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder) {
-        ((ContainerNode) dataNode).getValue().forEach(mapEntryBuilder::addChild);
+    private static void mapRpcDataNode(final @NonNull NormalizedNode dataNode,
+            final @NonNull DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder) {
+        ((ContainerNode) dataNode).body().forEach(mapEntryBuilder::addChild);
     }
 
     /**
      * Prepare and map all data from DS.
      *
-     * @param configDataNode
-     *             data node of config data
-     * @param stateDataNode
-     *             data node of state data
+     * @param configDataNode data node of config data
+     * @param stateDataNode  data node of state data
      * @return {@link NormalizedNode}
      */
-    @Nonnull
-    private static NormalizedNode<?, ?> prepareData(@Nonnull final NormalizedNode<?, ?> configDataNode,
-                                                             @Nonnull final NormalizedNode<?, ?> stateDataNode) {
-        if (configDataNode instanceof MapNode) {
-            final CollectionNodeBuilder<MapEntryNode, MapNode> builder = ImmutableNodes
+    @SuppressWarnings("unchecked")
+    private static @NonNull NormalizedNode prepareData(final @NonNull NormalizedNode configDataNode,
+                                                       final @NonNull NormalizedNode stateDataNode) {
+        if (configDataNode instanceof UserMapNode) {
+            final CollectionNodeBuilder<MapEntryNode, UserMapNode> builder = Builders
+                    .orderedMapBuilder().withNodeIdentifier(((MapNode) configDataNode).getIdentifier());
+
+            mapValueToBuilder(
+                    ((UserMapNode) configDataNode).body(), ((UserMapNode) stateDataNode).body(), builder);
+
+            return builder.build();
+        } else if (configDataNode instanceof MapNode) {
+            final CollectionNodeBuilder<MapEntryNode, SystemMapNode> builder = ImmutableNodes
                     .mapNodeBuilder().withNodeIdentifier(((MapNode) configDataNode).getIdentifier());
 
             mapValueToBuilder(
-                    ((MapNode) configDataNode).getValue(), ((MapNode) stateDataNode).getValue(), builder);
+                    ((MapNode) configDataNode).body(), ((MapNode) stateDataNode).body(), builder);
 
             return builder.build();
         } else if (configDataNode instanceof MapEntryNode) {
@@ -591,23 +464,23 @@ public final class ReadDataTransactionUtil {
                     .mapEntryBuilder().withNodeIdentifier(((MapEntryNode) configDataNode).getIdentifier());
 
             mapValueToBuilder(
-                    ((MapEntryNode) configDataNode).getValue(), ((MapEntryNode) stateDataNode).getValue(), builder);
+                    ((MapEntryNode) configDataNode).body(), ((MapEntryNode) stateDataNode).body(), builder);
 
             return builder.build();
         } else if (configDataNode instanceof ContainerNode) {
-            final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder = Builders
+            final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder = Builders
                     .containerBuilder().withNodeIdentifier(((ContainerNode) configDataNode).getIdentifier());
 
             mapValueToBuilder(
-                    ((ContainerNode) configDataNode).getValue(), ((ContainerNode) stateDataNode).getValue(), builder);
+                    ((ContainerNode) configDataNode).body(), ((ContainerNode) stateDataNode).body(), builder);
 
             return builder.build();
         } else if (configDataNode instanceof AugmentationNode) {
             final DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> builder = Builders
                     .augmentationBuilder().withNodeIdentifier(((AugmentationNode) configDataNode).getIdentifier());
 
-            mapValueToBuilder(((AugmentationNode) configDataNode).getValue(),
-                    ((AugmentationNode) stateDataNode).getValue(), builder);
+            mapValueToBuilder(((AugmentationNode) configDataNode).body(),
+                    ((AugmentationNode) stateDataNode).body(), builder);
 
             return builder.build();
         } else if (configDataNode instanceof ChoiceNode) {
@@ -615,30 +488,59 @@ public final class ReadDataTransactionUtil {
                     .choiceBuilder().withNodeIdentifier(((ChoiceNode) configDataNode).getIdentifier());
 
             mapValueToBuilder(
-                    ((ChoiceNode) configDataNode).getValue(), ((ChoiceNode) stateDataNode).getValue(), builder);
+                    ((ChoiceNode) configDataNode).body(), ((ChoiceNode) stateDataNode).body(), builder);
 
             return builder.build();
         } else if (configDataNode instanceof LeafNode) {
-            return ImmutableNodes.leafNode(configDataNode.getNodeType(), configDataNode.getValue());
+            return ImmutableNodes.leafNode(configDataNode.getIdentifier().getNodeType(), configDataNode.body());
+        } else if (configDataNode instanceof UserLeafSetNode) {
+            final ListNodeBuilder<Object, UserLeafSetNode<Object>> builder = Builders
+                .orderedLeafSetBuilder().withNodeIdentifier(((UserLeafSetNode<?>) configDataNode).getIdentifier());
+
+            mapValueToBuilder(((UserLeafSetNode<Object>) configDataNode).body(),
+                    ((UserLeafSetNode<Object>) stateDataNode).body(), builder);
+            return builder.build();
+        } else if (configDataNode instanceof LeafSetNode) {
+            final ListNodeBuilder<Object, SystemLeafSetNode<Object>> builder = Builders
+                    .leafSetBuilder().withNodeIdentifier(((LeafSetNode<?>) configDataNode).getIdentifier());
+
+            mapValueToBuilder(((LeafSetNode<Object>) configDataNode).body(),
+                    ((LeafSetNode<Object>) stateDataNode).body(), builder);
+            return builder.build();
+        } else if (configDataNode instanceof LeafSetEntryNode) {
+            return Builders.leafSetEntryBuilder()
+                    .withNodeIdentifier(((LeafSetEntryNode<?>) configDataNode).getIdentifier())
+                    .withValue(configDataNode.body())
+                    .build();
+        } else if (configDataNode instanceof UnkeyedListNode) {
+            final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> builder = Builders
+                    .unkeyedListBuilder().withNodeIdentifier(((UnkeyedListNode) configDataNode).getIdentifier());
+
+            mapValueToBuilder(((UnkeyedListNode) configDataNode).body(),
+                    ((UnkeyedListNode) stateDataNode).body(), builder);
+            return builder.build();
+        } else if (configDataNode instanceof UnkeyedListEntryNode) {
+            final DataContainerNodeBuilder<NodeIdentifier, UnkeyedListEntryNode> builder = Builders
+                .unkeyedListEntryBuilder().withNodeIdentifier(((UnkeyedListEntryNode) configDataNode).getIdentifier());
+
+            mapValueToBuilder(((UnkeyedListEntryNode) configDataNode).body(),
+                    ((UnkeyedListEntryNode) stateDataNode).body(), builder);
+            return builder.build();
         } else {
-            throw new RestconfDocumentedException("Bad type of node.");
+            throw new RestconfDocumentedException("Unexpected node type: " + configDataNode.getClass().getName());
         }
     }
 
     /**
      * Map value from container node to builder.
      *
-     * @param configData
-     *             collection of config data nodes
-     * @param stateData
-     *             collection of state data nodes
-     * @param builder
-     *             builder
+     * @param configData collection of config data nodes
+     * @param stateData  collection of state data nodes
+     * @param builder    builder
      */
-    private static <T extends NormalizedNode<? extends PathArgument, ?>> void mapValueToBuilder(
-            @Nonnull final Collection<T> configData,
-            @Nonnull final Collection<T> stateData,
-            @Nonnull final NormalizedNodeContainerBuilder<?, PathArgument, T, ?> builder) {
+    private static <T extends NormalizedNode> void mapValueToBuilder(
+            final @NonNull Collection<T> configData, final @NonNull Collection<T> stateData,
+            final @NonNull NormalizedNodeContainerBuilder<?, PathArgument, T, ?> builder) {
         final Map<PathArgument, T> configMap = configData.stream().collect(
                 Collectors.toMap(NormalizedNode::getIdentifier, Function.identity()));
         final Map<PathArgument, T> stateMap = stateData.stream().collect(
@@ -655,17 +557,13 @@ public final class ReadDataTransactionUtil {
      * Map data with different identifiers to builder. Data with different identifiers can be just added
      * as childs to parent node.
      *
-     * @param configMap
-     *             map of config data nodes
-     * @param stateMap
-     *             map of state data nodes
-     * @param builder
-     *           - builder
+     * @param configMap map of config data nodes
+     * @param stateMap  map of state data nodes
+     * @param builder   - builder
      */
-    private static <T extends NormalizedNode<? extends PathArgument, ?>> void mapDataToBuilder(
-            @Nonnull final Map<PathArgument, T> configMap,
-            @Nonnull final Map<PathArgument, T> stateMap,
-            @Nonnull final NormalizedNodeContainerBuilder<?, PathArgument, T, ?> builder) {
+    private static <T extends NormalizedNode> void mapDataToBuilder(
+            final @NonNull Map<PathArgument, T> configMap, final @NonNull Map<PathArgument, T> stateMap,
+            final @NonNull NormalizedNodeContainerBuilder<?, PathArgument, T, ?> builder) {
         configMap.entrySet().stream().filter(x -> !stateMap.containsKey(x.getKey())).forEach(
             y -> builder.addChild(y.getValue()));
         stateMap.entrySet().stream().filter(x -> !configMap.containsKey(x.getKey())).forEach(
@@ -676,18 +574,14 @@ public final class ReadDataTransactionUtil {
      * Map data with the same identifiers to builder. Data with the same identifiers cannot be just added but we need to
      * go one level down with {@code prepareData} method.
      *
-     * @param configMap
-     *             immutable config data
-     * @param stateMap
-     *             immutable state data
-     * @param builder
-     *           - builder
+     * @param configMap immutable config data
+     * @param stateMap  immutable state data
+     * @param builder   - builder
      */
     @SuppressWarnings("unchecked")
-    private static <T extends NormalizedNode<? extends PathArgument, ?>> void mergeDataToBuilder(
-            @Nonnull final Map<PathArgument, T> configMap,
-            @Nonnull final Map<PathArgument, T> stateMap,
-            @Nonnull final NormalizedNodeContainerBuilder<?, PathArgument, T, ?> builder) {
+    private static <T extends NormalizedNode> void mergeDataToBuilder(
+            final @NonNull Map<PathArgument, T> configMap, final @NonNull Map<PathArgument, T> stateMap,
+            final @NonNull NormalizedNodeContainerBuilder<?, PathArgument, T, ?> builder) {
         // it is enough to process only config data because operational contains the same data
         configMap.entrySet().stream().filter(x -> stateMap.containsKey(x.getKey())).forEach(
             y -> builder.addChild((T) prepareData(y.getValue(), stateMap.get(y.getKey()))));