98ea8a346e503881d112f21169b639e79def53ba
[netconf.git] / restconf / restconf-nb-rfc8040 / src / main / java / org / opendaylight / restconf / nb / rfc8040 / rests / utils / ReadDataTransactionUtil.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.restconf.nb.rfc8040.rests.utils;
9
10 import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.STREAMS_PATH;
11 import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.STREAM_PATH_PART;
12
13 import com.google.common.collect.Iterables;
14 import com.google.common.primitives.Ints;
15 import com.google.common.util.concurrent.FluentFuture;
16 import java.net.URI;
17 import java.util.Collection;
18 import java.util.Collections;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Optional;
22 import java.util.function.Function;
23 import java.util.stream.Collectors;
24 import javax.ws.rs.core.UriInfo;
25 import org.eclipse.jdt.annotation.NonNull;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
28 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
29 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
30 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
31 import org.opendaylight.restconf.common.context.WriterParameters;
32 import org.opendaylight.restconf.common.context.WriterParameters.WriterParametersBuilder;
33 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
34 import org.opendaylight.restconf.common.errors.RestconfError;
35 import org.opendaylight.restconf.nb.rfc8040.references.SchemaContextRef;
36 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
37 import org.opendaylight.restconf.nb.rfc8040.streams.listeners.NotificationListenerAdapter;
38 import org.opendaylight.restconf.nb.rfc8040.utils.mapping.RestconfMappingNodeUtil;
39 import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserFieldsParameter;
40 import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
41 import org.opendaylight.yangtools.yang.common.QName;
42 import org.opendaylight.yangtools.yang.common.QNameModule;
43 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
44 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
45 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
46 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
47 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
48 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
49 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
50 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
51 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
52 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
53 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
54 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
55 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
56 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
57 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
58 import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
59 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
60 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
61 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
62 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
63 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
64 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
65 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
66 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
67 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
68 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
69 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
70 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
71 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
72 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
73 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
74 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
75 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
76 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
77 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
78
79 /**
80  * Util class for read data from data store via transaction.
81  * <ul>
82  * <li>config
83  * <li>state
84  * <li>all (config + state)
85  * </ul>
86  *
87  */
88 public final class ReadDataTransactionUtil {
89
90     private ReadDataTransactionUtil() {
91         throw new UnsupportedOperationException("Util class.");
92     }
93
94     /**
95      * Parse parameters from URI request and check their types and values.
96      *
97      *
98      * @param identifier
99      *             {@link InstanceIdentifierContext}
100      * @param uriInfo
101      *             URI info
102      * @param tagged
103      *             set tagged for {@link WriterParameters}
104      * @return {@link WriterParameters}
105      */
106     public static @NonNull WriterParameters parseUriParameters(final @NonNull InstanceIdentifierContext<?> identifier,
107             final @Nullable UriInfo uriInfo, final boolean tagged) {
108         return parseParams(identifier, uriInfo, tagged);
109     }
110
111     /**
112      * Parse parameters from URI request and check their types and values.
113      *
114      *
115      * @param identifier
116      *             {@link InstanceIdentifierContext}
117      * @param uriInfo
118      *             URI info
119      * @return {@link WriterParameters}
120      */
121     public static @NonNull WriterParameters parseUriParameters(final @NonNull InstanceIdentifierContext<?> identifier,
122                                                                final @Nullable UriInfo uriInfo) {
123         return parseParams(identifier, uriInfo, false);
124     }
125
126     private static WriterParameters parseParams(final InstanceIdentifierContext<?> identifier, final UriInfo uriInfo,
127             final boolean tagged) {
128         final WriterParametersBuilder builder = new WriterParametersBuilder();
129         builder.setTagged(tagged);
130
131         if (uriInfo == null) {
132             return builder.build();
133         }
134
135         // check only allowed parameters
136         ParametersUtil.checkParametersTypes(
137                 RestconfDataServiceConstant.ReadData.READ_TYPE_TX,
138                 uriInfo.getQueryParameters().keySet(),
139                 RestconfDataServiceConstant.ReadData.CONTENT,
140                 RestconfDataServiceConstant.ReadData.DEPTH,
141                 RestconfDataServiceConstant.ReadData.FIELDS, RestconfDataServiceConstant.ReadData.WITH_DEFAULTS);
142
143         // read parameters from URI or set default values
144         final List<String> content = uriInfo.getQueryParameters().getOrDefault(
145                 RestconfDataServiceConstant.ReadData.CONTENT,
146                 Collections.singletonList(RestconfDataServiceConstant.ReadData.ALL));
147         final List<String> depth = uriInfo.getQueryParameters().getOrDefault(
148                 RestconfDataServiceConstant.ReadData.DEPTH,
149                 Collections.singletonList(RestconfDataServiceConstant.ReadData.UNBOUNDED));
150         // fields
151         final List<String> fields = uriInfo.getQueryParameters().getOrDefault(
152                 RestconfDataServiceConstant.ReadData.FIELDS,
153                 Collections.emptyList());
154
155         // parameter can be in URI at most once
156         ParametersUtil.checkParameterCount(content, RestconfDataServiceConstant.ReadData.CONTENT);
157         ParametersUtil.checkParameterCount(depth, RestconfDataServiceConstant.ReadData.DEPTH);
158         ParametersUtil.checkParameterCount(fields, RestconfDataServiceConstant.ReadData.FIELDS);
159
160         // check and set content
161         final String contentValue = content.get(0);
162         if (!contentValue.equals(RestconfDataServiceConstant.ReadData.ALL)) {
163             if (!contentValue.equals(RestconfDataServiceConstant.ReadData.CONFIG)
164                     && !contentValue.equals(RestconfDataServiceConstant.ReadData.NONCONFIG)) {
165                 throw new RestconfDocumentedException(
166                         new RestconfError(RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.INVALID_VALUE,
167                                 "Invalid content parameter: " + contentValue, null,
168                                 "The content parameter value must be either config, nonconfig or all (default)"));
169             }
170         }
171
172         builder.setContent(content.get(0));
173
174         // check and set depth
175         if (!depth.get(0).equals(RestconfDataServiceConstant.ReadData.UNBOUNDED)) {
176             final Integer value = Ints.tryParse(depth.get(0));
177
178             if (value == null
179                     || !(value >= RestconfDataServiceConstant.ReadData.MIN_DEPTH
180                         && value <= RestconfDataServiceConstant.ReadData.MAX_DEPTH)) {
181                 throw new RestconfDocumentedException(
182                         new RestconfError(RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.INVALID_VALUE,
183                                 "Invalid depth parameter: " + depth, null,
184                                 "The depth parameter must be an integer between 1 and 65535 or \"unbounded\""));
185             } else {
186                 builder.setDepth(value);
187             }
188         }
189
190         // check and set fields
191         if (!fields.isEmpty()) {
192             builder.setFields(ParserFieldsParameter.parseFieldsParameter(identifier, fields.get(0)));
193         }
194
195         return builder.build();
196     }
197
198     /**
199      * Read specific type of data from data store via transaction.
200      *
201      * @param valueOfContent
202      *            type of data to read (config, state, all)
203      * @param transactionNode
204      *            {@link TransactionVarsWrapper} - wrapper for variables
205      * @param schemaContext
206      *            schema context
207      * @return {@link NormalizedNode}
208      */
209     public static @Nullable NormalizedNode<?, ?> readData(final @NonNull String valueOfContent,
210             final @NonNull TransactionVarsWrapper transactionNode, final SchemaContext schemaContext) {
211         return readData(valueOfContent, transactionNode, null, schemaContext);
212     }
213
214     /**
215      * Read specific type of data from data store via transaction.
216      *
217      * @param valueOfContent
218      *            type of data to read (config, state, all)
219      * @param transactionNode
220      *            {@link TransactionVarsWrapper} - wrapper for variables
221      * @param withDefa
222      *            vaule of with-defaults parameter
223      * @param ctx
224      *            schema context
225      * @return {@link NormalizedNode}
226      */
227     public static @Nullable NormalizedNode<?, ?> readData(final @NonNull String valueOfContent,
228             final @NonNull TransactionVarsWrapper transactionNode, final String withDefa, final SchemaContext ctx) {
229         switch (valueOfContent) {
230             case RestconfDataServiceConstant.ReadData.CONFIG:
231                 transactionNode.setLogicalDatastoreType(LogicalDatastoreType.CONFIGURATION);
232                 if (withDefa == null) {
233                     return readDataViaTransaction(transactionNode);
234                 } else {
235                     return prepareDataByParamWithDef(readDataViaTransaction(transactionNode),
236                             transactionNode.getInstanceIdentifier().getInstanceIdentifier(), withDefa, ctx);
237                 }
238             case RestconfDataServiceConstant.ReadData.NONCONFIG:
239                 transactionNode.setLogicalDatastoreType(LogicalDatastoreType.OPERATIONAL);
240                 return readDataViaTransaction(transactionNode);
241
242             case RestconfDataServiceConstant.ReadData.ALL:
243                 return readAllData(transactionNode, withDefa, ctx);
244
245             default:
246                 throw new RestconfDocumentedException(
247                         new RestconfError(RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.INVALID_VALUE,
248                                 "Invalid content parameter: " + valueOfContent, null,
249                                 "The content parameter value must be either config, nonconfig or all (default)"));
250         }
251     }
252
253     /**
254      * Read specific type of data from data store via transaction and if identifier read data from
255      * streams then put streams from actual schema context to datastore.
256      *
257      * @param identifier
258      *             identifier of data to read
259      * @param content
260      *             type of data to read (config, state, all)
261      * @param transactionNode
262      *             {@link TransactionVarsWrapper} - wrapper for variables
263      * @param withDefa
264      *             vaule of with-defaults parameter
265      * @param schemaContextRef
266      *             schema context
267      * @param uriInfo
268      *             uri info
269      * @return {@link NormalizedNode}
270      */
271     public static NormalizedNode<?, ?> readData(final String identifier, final String content,
272                                                 final TransactionVarsWrapper transactionNode, final String withDefa,
273                                                 final SchemaContextRef schemaContextRef, final UriInfo uriInfo) {
274         final SchemaContext schemaContext = schemaContextRef.get();
275         if (identifier.contains(STREAMS_PATH) && !identifier.contains(STREAM_PATH_PART)) {
276             final DOMDataTreeReadWriteTransaction wTx = transactionNode.getTransactionChain().newReadWriteTransaction();
277             final boolean exist = SubscribeToStreamUtil.checkExist(schemaContext, wTx);
278
279             for (final NotificationDefinition notificationDefinition : schemaContextRef.get().getNotifications()) {
280                 final List<NotificationListenerAdapter> notifiStreamXML =
281                         CreateStreamUtil.createYangNotifiStream(notificationDefinition, schemaContextRef,
282                                 NotificationOutputType.XML.getName());
283                 final List<NotificationListenerAdapter> notifiStreamJSON =
284                         CreateStreamUtil.createYangNotifiStream(notificationDefinition, schemaContextRef,
285                                 NotificationOutputType.JSON.getName());
286                 for (final NotificationListenerAdapter listener : Iterables.concat(notifiStreamXML, notifiStreamJSON)) {
287                     final URI uri = SubscribeToStreamUtil.prepareUriByStreamName(uriInfo, listener.getStreamName());
288                     final NormalizedNode mapToStreams =
289                             RestconfMappingNodeUtil.mapYangNotificationStreamByIetfRestconfMonitoring(
290                                     listener.getSchemaPath().getLastComponent(), schemaContext.getNotifications(),
291                                     null, listener.getOutputType(), uri,
292                                     SubscribeToStreamUtil.getMonitoringModule(schemaContext), exist);
293                     SubscribeToStreamUtil.writeDataToDS(schemaContext,
294                             listener.getSchemaPath().getLastComponent().getLocalName(), wTx, exist,
295                             mapToStreams);
296                 }
297             }
298             SubscribeToStreamUtil.submitData(wTx);
299         }
300         return readData(content, transactionNode, withDefa, schemaContext);
301     }
302
303     private static NormalizedNode<?, ?> prepareDataByParamWithDef(final NormalizedNode<?, ?> result,
304             final YangInstanceIdentifier path, final String withDefa, final SchemaContext ctx) {
305         boolean trim;
306         switch (withDefa) {
307             case "trim":
308                 trim = true;
309                 break;
310             case "explicit":
311                 trim = false;
312                 break;
313             default:
314                 throw new RestconfDocumentedException("");
315         }
316
317         final DataSchemaContextTree baseSchemaCtxTree = DataSchemaContextTree.from(ctx);
318         final DataSchemaNode baseSchemaNode = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
319         if (result instanceof ContainerNode) {
320             final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder =
321                     Builders.containerBuilder((ContainerSchemaNode) baseSchemaNode);
322             buildCont(builder, (ContainerNode) result, baseSchemaCtxTree, path, trim);
323             return builder.build();
324         } else {
325             final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
326                     Builders.mapEntryBuilder((ListSchemaNode) baseSchemaNode);
327             buildMapEntryBuilder(builder, (MapEntryNode) result, baseSchemaCtxTree, path, trim,
328                     ((ListSchemaNode) baseSchemaNode).getKeyDefinition());
329             return builder.build();
330         }
331     }
332
333     private static void buildMapEntryBuilder(
334             final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder,
335             final MapEntryNode result, final DataSchemaContextTree baseSchemaCtxTree,
336             final YangInstanceIdentifier actualPath, final boolean trim, final List<QName> keys) {
337         for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
338             final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
339             final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
340             if (child instanceof ContainerNode) {
341                 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> childBuilder =
342                         Builders.containerBuilder((ContainerSchemaNode) childSchema);
343                 buildCont(childBuilder, (ContainerNode) child, baseSchemaCtxTree, path, trim);
344                 builder.withChild(childBuilder.build());
345             } else if (child instanceof MapNode) {
346                 final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
347                         Builders.mapBuilder((ListSchemaNode) childSchema);
348                 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
349                         ((ListSchemaNode) childSchema).getKeyDefinition());
350                 builder.withChild(childBuilder.build());
351             } else if (child instanceof LeafNode) {
352                 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
353                 final Object nodeVal = child.getValue();
354                 final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
355                         Builders.leafBuilder((LeafSchemaNode) childSchema);
356                 if (keys.contains(child.getNodeType())) {
357                     leafBuilder.withValue(((LeafNode) child).getValue());
358                     builder.withChild(leafBuilder.build());
359                 } else {
360                     if (trim) {
361                         if (defaultVal == null || !defaultVal.equals(nodeVal)) {
362                             leafBuilder.withValue(((LeafNode) child).getValue());
363                             builder.withChild(leafBuilder.build());
364                         }
365                     } else {
366                         if (defaultVal != null && defaultVal.equals(nodeVal)) {
367                             leafBuilder.withValue(((LeafNode) child).getValue());
368                             builder.withChild(leafBuilder.build());
369                         }
370                     }
371                 }
372             }
373         }
374     }
375
376     private static void buildList(final CollectionNodeBuilder<MapEntryNode, MapNode> builder, final MapNode result,
377             final DataSchemaContextTree baseSchemaCtxTree, final YangInstanceIdentifier path, final boolean trim,
378             final List<QName> keys) {
379         for (final MapEntryNode mapEntryNode : result.getValue()) {
380             final YangInstanceIdentifier actualNode = path.node(mapEntryNode.getIdentifier());
381             final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(actualNode).getDataSchemaNode();
382             final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
383                     Builders.mapEntryBuilder((ListSchemaNode) childSchema);
384             buildMapEntryBuilder(mapEntryBuilder, mapEntryNode, baseSchemaCtxTree, actualNode, trim, keys);
385             builder.withChild(mapEntryBuilder.build());
386         }
387     }
388
389     private static void buildCont(final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder,
390             final ContainerNode result, final DataSchemaContextTree baseSchemaCtxTree,
391             final YangInstanceIdentifier actualPath, final boolean trim) {
392         for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
393             final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
394             final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
395             if (child instanceof ContainerNode) {
396                 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builderChild =
397                         Builders.containerBuilder((ContainerSchemaNode) childSchema);
398                 buildCont(builderChild, result, baseSchemaCtxTree, actualPath, trim);
399                 builder.withChild(builderChild.build());
400             } else if (child instanceof MapNode) {
401                 final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
402                         Builders.mapBuilder((ListSchemaNode) childSchema);
403                 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
404                         ((ListSchemaNode) childSchema).getKeyDefinition());
405                 builder.withChild(childBuilder.build());
406             } else if (child instanceof LeafNode) {
407                 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
408                 final Object nodeVal = child.getValue();
409                 final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
410                         Builders.leafBuilder((LeafSchemaNode) childSchema);
411                 if (trim) {
412                     if (defaultVal == null || !defaultVal.equals(nodeVal)) {
413                         leafBuilder.withValue(((LeafNode) child).getValue());
414                         builder.withChild(leafBuilder.build());
415                     }
416                 } else {
417                     if (defaultVal != null && defaultVal.equals(nodeVal)) {
418                         leafBuilder.withValue(((LeafNode) child).getValue());
419                         builder.withChild(leafBuilder.build());
420                     }
421                 }
422             }
423         }
424     }
425
426     /**
427      * If is set specific {@link LogicalDatastoreType} in
428      * {@link TransactionVarsWrapper}, then read this type of data from DS. If
429      * don't, we have to read all data from DS (state + config)
430      *
431      * @param transactionNode
432      *             {@link TransactionVarsWrapper} - wrapper for variables
433      * @return {@link NormalizedNode}
434      */
435     private static @Nullable NormalizedNode<?, ?> readDataViaTransaction(
436             final @NonNull TransactionVarsWrapper transactionNode) {
437         final NormalizedNodeFactory dataFactory = new NormalizedNodeFactory();
438         try (DOMDataTreeReadTransaction tx = transactionNode.getTransactionChain().newReadOnlyTransaction()) {
439             final FluentFuture<Optional<NormalizedNode<?, ?>>> listenableFuture = tx.read(
440                 transactionNode.getLogicalDatastoreType(),
441                 transactionNode.getInstanceIdentifier().getInstanceIdentifier());
442             FutureCallbackTx.addCallback(listenableFuture, RestconfDataServiceConstant.ReadData.READ_TYPE_TX,
443                 dataFactory);
444         }
445         return dataFactory.build();
446     }
447
448     /**
449      * Read config and state data, then map them.
450      *
451      * @param transactionNode
452      *            {@link TransactionVarsWrapper} - wrapper for variables
453      * @param withDefa
454      *            with-defaults parameter
455      * @param ctx
456      *            schema context
457      * @return {@link NormalizedNode}
458      */
459     private static @Nullable NormalizedNode<?, ?> readAllData(final @NonNull TransactionVarsWrapper transactionNode,
460             final String withDefa, final SchemaContext ctx) {
461         // PREPARE STATE DATA NODE
462         transactionNode.setLogicalDatastoreType(LogicalDatastoreType.OPERATIONAL);
463         final NormalizedNode<?, ?> stateDataNode = readDataViaTransaction(transactionNode);
464
465         // PREPARE CONFIG DATA NODE
466         transactionNode.setLogicalDatastoreType(LogicalDatastoreType.CONFIGURATION);
467         final NormalizedNode<?, ?> configDataNode;
468         if (withDefa == null) {
469             configDataNode = readDataViaTransaction(transactionNode);
470         } else {
471             configDataNode = prepareDataByParamWithDef(readDataViaTransaction(transactionNode),
472                     transactionNode.getInstanceIdentifier().getInstanceIdentifier(), withDefa, ctx);
473         }
474
475         // if no data exists
476         if (stateDataNode == null && configDataNode == null) {
477             return null;
478         }
479
480         // return config data
481         if (stateDataNode == null) {
482             return configDataNode;
483         }
484
485         // return state data
486         if (configDataNode == null) {
487             return stateDataNode;
488         }
489
490         // merge data from config and state
491         return mergeStateAndConfigData(stateDataNode, configDataNode);
492     }
493
494     /**
495      * Merge state and config data into a single NormalizedNode.
496      *
497      * @param stateDataNode
498      *             data node of state data
499      * @param configDataNode
500      *             data node of config data
501      * @return {@link NormalizedNode}
502      */
503     private static @NonNull NormalizedNode<?, ?> mergeStateAndConfigData(
504             final @NonNull NormalizedNode<?, ?> stateDataNode, final @NonNull NormalizedNode<?, ?> configDataNode) {
505         validateNodeMerge(stateDataNode, configDataNode);
506         if (configDataNode instanceof RpcDefinition) {
507             return prepareRpcData(configDataNode, stateDataNode);
508         } else {
509             return prepareData(configDataNode, stateDataNode);
510         }
511     }
512
513     /**
514      * Validates whether the two NormalizedNodes can be merged.
515      *
516      * @param stateDataNode
517      *             data node of state data
518      * @param configDataNode
519      *             data node of config data
520      */
521     private static void validateNodeMerge(final @NonNull NormalizedNode<?, ?> stateDataNode,
522                                           final @NonNull NormalizedNode<?, ?> configDataNode) {
523         final QNameModule moduleOfStateData = stateDataNode.getIdentifier().getNodeType().getModule();
524         final QNameModule moduleOfConfigData = configDataNode.getIdentifier().getNodeType().getModule();
525         if (!moduleOfStateData.equals(moduleOfConfigData)) {
526             throw new RestconfDocumentedException("Unable to merge data from different modules.");
527         }
528     }
529
530     /**
531      * Prepare and map data for rpc.
532      *
533      * @param configDataNode
534      *             data node of config data
535      * @param stateDataNode
536      *             data node of state data
537      * @return {@link NormalizedNode}
538      */
539     private static @NonNull NormalizedNode<?, ?> prepareRpcData(final @NonNull NormalizedNode<?, ?> configDataNode,
540                                                                 final @NonNull NormalizedNode<?, ?> stateDataNode) {
541         final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder = ImmutableNodes
542                 .mapEntryBuilder();
543         mapEntryBuilder.withNodeIdentifier((NodeIdentifierWithPredicates) configDataNode.getIdentifier());
544
545         // MAP CONFIG DATA
546         mapRpcDataNode(configDataNode, mapEntryBuilder);
547         // MAP STATE DATA
548         mapRpcDataNode(stateDataNode, mapEntryBuilder);
549
550         return ImmutableNodes.mapNodeBuilder(configDataNode.getNodeType()).addChild(mapEntryBuilder.build()).build();
551     }
552
553     /**
554      * Map node to map entry builder.
555      *
556      * @param dataNode
557      *             data node
558      * @param mapEntryBuilder
559      *             builder for mapping data
560      */
561     private static void mapRpcDataNode(final @NonNull NormalizedNode<?, ?> dataNode,
562             final @NonNull DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder) {
563         ((ContainerNode) dataNode).getValue().forEach(mapEntryBuilder::addChild);
564     }
565
566     /**
567      * Prepare and map all data from DS.
568      *
569      * @param configDataNode
570      *             data node of config data
571      * @param stateDataNode
572      *             data node of state data
573      * @return {@link NormalizedNode}
574      */
575     @SuppressWarnings("unchecked")
576     private static @NonNull NormalizedNode<?, ?> prepareData(final @NonNull NormalizedNode<?, ?> configDataNode,
577                                                              final @NonNull NormalizedNode<?, ?> stateDataNode) {
578         if (configDataNode instanceof OrderedMapNode) {
579             final CollectionNodeBuilder<MapEntryNode, OrderedMapNode> builder = Builders
580                     .orderedMapBuilder().withNodeIdentifier(((MapNode) configDataNode).getIdentifier());
581
582             mapValueToBuilder(
583                     ((OrderedMapNode) configDataNode).getValue(), ((OrderedMapNode) stateDataNode).getValue(), builder);
584
585             return builder.build();
586         } else if (configDataNode instanceof MapNode) {
587             final CollectionNodeBuilder<MapEntryNode, MapNode> builder = ImmutableNodes
588                     .mapNodeBuilder().withNodeIdentifier(((MapNode) configDataNode).getIdentifier());
589
590             mapValueToBuilder(
591                     ((MapNode) configDataNode).getValue(), ((MapNode) stateDataNode).getValue(), builder);
592
593             return builder.build();
594         } else if (configDataNode instanceof MapEntryNode) {
595             final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = ImmutableNodes
596                     .mapEntryBuilder().withNodeIdentifier(((MapEntryNode) configDataNode).getIdentifier());
597
598             mapValueToBuilder(
599                     ((MapEntryNode) configDataNode).getValue(), ((MapEntryNode) stateDataNode).getValue(), builder);
600
601             return builder.build();
602         } else if (configDataNode instanceof ContainerNode) {
603             final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder = Builders
604                     .containerBuilder().withNodeIdentifier(((ContainerNode) configDataNode).getIdentifier());
605
606             mapValueToBuilder(
607                     ((ContainerNode) configDataNode).getValue(), ((ContainerNode) stateDataNode).getValue(), builder);
608
609             return builder.build();
610         } else if (configDataNode instanceof AugmentationNode) {
611             final DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> builder = Builders
612                     .augmentationBuilder().withNodeIdentifier(((AugmentationNode) configDataNode).getIdentifier());
613
614             mapValueToBuilder(((AugmentationNode) configDataNode).getValue(),
615                     ((AugmentationNode) stateDataNode).getValue(), builder);
616
617             return builder.build();
618         } else if (configDataNode instanceof ChoiceNode) {
619             final DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> builder = Builders
620                     .choiceBuilder().withNodeIdentifier(((ChoiceNode) configDataNode).getIdentifier());
621
622             mapValueToBuilder(
623                     ((ChoiceNode) configDataNode).getValue(), ((ChoiceNode) stateDataNode).getValue(), builder);
624
625             return builder.build();
626         } else if (configDataNode instanceof LeafNode) {
627             return ImmutableNodes.leafNode(configDataNode.getNodeType(), configDataNode.getValue());
628         } else if (configDataNode instanceof OrderedLeafSetNode) {
629             final ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder = Builders
630                 .orderedLeafSetBuilder().withNodeIdentifier(((OrderedLeafSetNode<?>) configDataNode).getIdentifier());
631
632             mapValueToBuilder(((OrderedLeafSetNode<Object>) configDataNode).getValue(),
633                     ((OrderedLeafSetNode<Object>) stateDataNode).getValue(), builder);
634             return builder.build();
635         } else if (configDataNode instanceof LeafSetNode) {
636             final ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder = Builders
637                     .leafSetBuilder().withNodeIdentifier(((LeafSetNode<?>) configDataNode).getIdentifier());
638
639             mapValueToBuilder(((LeafSetNode<Object>) configDataNode).getValue(),
640                     ((LeafSetNode<Object>) stateDataNode).getValue(), builder);
641             return builder.build();
642         } else if (configDataNode instanceof UnkeyedListNode) {
643             final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> builder = Builders
644                     .unkeyedListBuilder().withNodeIdentifier(((UnkeyedListNode) configDataNode).getIdentifier());
645
646             mapValueToBuilder(((UnkeyedListNode) configDataNode).getValue(),
647                     ((UnkeyedListNode) stateDataNode).getValue(), builder);
648             return builder.build();
649         } else if (configDataNode instanceof UnkeyedListEntryNode) {
650             final DataContainerNodeAttrBuilder<NodeIdentifier, UnkeyedListEntryNode> builder = Builders
651                 .unkeyedListEntryBuilder().withNodeIdentifier(((UnkeyedListEntryNode) configDataNode).getIdentifier());
652
653             mapValueToBuilder(((UnkeyedListEntryNode) configDataNode).getValue(),
654                     ((UnkeyedListEntryNode) stateDataNode).getValue(), builder);
655             return builder.build();
656         } else {
657             throw new RestconfDocumentedException("Unexpected node type: " + configDataNode.getClass().getName());
658         }
659     }
660
661     /**
662      * Map value from container node to builder.
663      *
664      * @param configData
665      *             collection of config data nodes
666      * @param stateData
667      *             collection of state data nodes
668      * @param builder
669      *             builder
670      */
671     private static <T extends NormalizedNode<? extends PathArgument, ?>> void mapValueToBuilder(
672             final @NonNull Collection<T> configData, final @NonNull Collection<T> stateData,
673             final @NonNull NormalizedNodeContainerBuilder<?, PathArgument, T, ?> builder) {
674         final Map<PathArgument, T> configMap = configData.stream().collect(
675                 Collectors.toMap(NormalizedNode::getIdentifier, Function.identity()));
676         final Map<PathArgument, T> stateMap = stateData.stream().collect(
677                 Collectors.toMap(NormalizedNode::getIdentifier, Function.identity()));
678
679         // merge config and state data of children with different identifiers
680         mapDataToBuilder(configMap, stateMap, builder);
681
682         // merge config and state data of children with the same identifiers
683         mergeDataToBuilder(configMap, stateMap, builder);
684     }
685
686     /**
687      * Map data with different identifiers to builder. Data with different identifiers can be just added
688      * as childs to parent node.
689      *
690      * @param configMap
691      *             map of config data nodes
692      * @param stateMap
693      *             map of state data nodes
694      * @param builder
695      *           - builder
696      */
697     private static <T extends NormalizedNode<? extends PathArgument, ?>> void mapDataToBuilder(
698             final @NonNull Map<PathArgument, T> configMap, final @NonNull Map<PathArgument, T> stateMap,
699             final @NonNull NormalizedNodeContainerBuilder<?, PathArgument, T, ?> builder) {
700         configMap.entrySet().stream().filter(x -> !stateMap.containsKey(x.getKey())).forEach(
701             y -> builder.addChild(y.getValue()));
702         stateMap.entrySet().stream().filter(x -> !configMap.containsKey(x.getKey())).forEach(
703             y -> builder.addChild(y.getValue()));
704     }
705
706     /**
707      * Map data with the same identifiers to builder. Data with the same identifiers cannot be just added but we need to
708      * go one level down with {@code prepareData} method.
709      *
710      * @param configMap
711      *             immutable config data
712      * @param stateMap
713      *             immutable state data
714      * @param builder
715      *           - builder
716      */
717     @SuppressWarnings("unchecked")
718     private static <T extends NormalizedNode<? extends PathArgument, ?>> void mergeDataToBuilder(
719             final @NonNull Map<PathArgument, T> configMap, final @NonNull Map<PathArgument, T> stateMap,
720             final @NonNull NormalizedNodeContainerBuilder<?, PathArgument, T, ?> builder) {
721         // it is enough to process only config data because operational contains the same data
722         configMap.entrySet().stream().filter(x -> stateMap.containsKey(x.getKey())).forEach(
723             y -> builder.addChild((T) prepareData(y.getValue(), stateMap.get(y.getKey()))));
724     }
725 }