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