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