Merge "Fix error reporting for PUT/POST"
[netconf.git] / restconf / sal-rest-connector / src / main / java / org / opendaylight / netconf / sal / restconf / impl / BrokerFacade.java
1 /**
2  * Copyright (c) 2014 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.netconf.sal.restconf.impl;
9
10 import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
11 import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
12
13 import com.google.common.base.Optional;
14 import com.google.common.base.Preconditions;
15 import com.google.common.collect.ImmutableList;
16 import com.google.common.util.concurrent.CheckedFuture;
17 import com.google.common.util.concurrent.FutureCallback;
18 import com.google.common.util.concurrent.Futures;
19 import java.util.ArrayList;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.concurrent.CountDownLatch;
23 import javax.annotation.Nullable;
24 import javax.ws.rs.core.Response.Status;
25 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
26 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
27 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
28 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
29 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
30 import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
31 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
32 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
33 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
34 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
35 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
36 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
37 import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
38 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
39 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
40 import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
41 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
42 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
43 import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
44 import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
45 import org.opendaylight.yangtools.concepts.ListenerRegistration;
46 import org.opendaylight.yangtools.yang.common.QName;
47 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
48 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
49 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
50 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
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.impl.schema.Builders;
62 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
63 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
64 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
65 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
66 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
67 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
68 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
69 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
70 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
71 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
72 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
73 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
74 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
75 import org.slf4j.Logger;
76 import org.slf4j.LoggerFactory;
77
78 public class BrokerFacade {
79     private final static Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
80
81     private final static BrokerFacade INSTANCE = new BrokerFacade();
82     private volatile DOMRpcService rpcService;
83     private volatile ConsumerSession context;
84     private DOMDataBroker domDataBroker;
85     private DOMNotificationService domNotification;
86
87     private BrokerFacade() {}
88
89     public void setRpcService(final DOMRpcService router) {
90         this.rpcService = router;
91     }
92
93     public void setDomNotificationService(final DOMNotificationService domNotification) {
94         this.domNotification = domNotification;
95     }
96
97     public void setContext(final ConsumerSession context) {
98         this.context = context;
99     }
100
101     public static BrokerFacade getInstance() {
102         return BrokerFacade.INSTANCE;
103     }
104
105     private void checkPreconditions() {
106         if (this.context == null || this.domDataBroker == null) {
107             throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
108         }
109     }
110
111     /**
112      * Read config data by path
113      *
114      * @param path
115      *            - path of data
116      * @return read date
117      */
118     public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path) {
119         return readConfigurationData(path, null);
120     }
121
122     /**
123      * Read config data by path
124      *
125      * @param path
126      *            - path of data
127      * @param withDefa
128      *            - value of with-defaults parameter
129      * @return read date
130      */
131     public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path, final String withDefa) {
132         checkPreconditions();
133         return readDataViaTransaction(this.domDataBroker.newReadOnlyTransaction(), CONFIGURATION, path, withDefa);
134     }
135
136     /**
137      * Read config data from mount point by path.
138      *
139      * @param mountPoint
140      *            - mount point for reading data
141      * @param path
142      *            - path of data
143      * @return read data
144      */
145     public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint,
146             final YangInstanceIdentifier path) {
147         return readConfigurationData(mountPoint, path, null);
148     }
149
150     /**
151      * Read config data from mount point by path.
152      *
153      * @param mountPoint
154      *            - mount point for reading data
155      * @param path
156      *            - path of data
157      * @param withDefa
158      *            - value of with-defaults parameter
159      * @return read data
160      */
161     public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path,
162             final String withDefa) {
163         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
164         if (domDataBrokerService.isPresent()) {
165             return readDataViaTransaction(domDataBrokerService.get().newReadOnlyTransaction(), CONFIGURATION, path,
166                     withDefa);
167         }
168         final String errMsg = "DOM data broker service isn't available for mount point " + path;
169         LOG.warn(errMsg);
170         throw new RestconfDocumentedException(errMsg);
171     }
172
173     /**
174      * Read operational data by path.
175      *
176      * @param path
177      *            - path of data
178      * @return read data
179      */
180     public NormalizedNode<?, ?> readOperationalData(final YangInstanceIdentifier path) {
181         checkPreconditions();
182         return readDataViaTransaction(this.domDataBroker.newReadOnlyTransaction(), OPERATIONAL, path);
183     }
184
185     /**
186      * Read operational data from mount point by path.
187      *
188      * @param mountPoint
189      *            - mount point for reading data
190      * @param path
191      *            - path of data
192      * @return read data
193      */
194     public NormalizedNode<?, ?> readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
195         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
196         if (domDataBrokerService.isPresent()) {
197             return readDataViaTransaction(domDataBrokerService.get().newReadOnlyTransaction(), OPERATIONAL, path);
198         }
199         final String errMsg = "DOM data broker service isn't available for mount point " + path;
200         LOG.warn(errMsg);
201         throw new RestconfDocumentedException(errMsg);
202     }
203
204     /**
205      * <b>PUT configuration data</b>
206      *
207      * Prepare result(status) for PUT operation and PUT data via transaction.
208      * Return wrapped status and future from PUT.
209      *
210      * @param globalSchema
211      *            - used by merge parents (if contains list)
212      * @param path
213      *            - path of node
214      * @param payload
215      *            - input data
216      * @param point
217      * @param insert
218      * @return wrapper of status and future of PUT
219      */
220     public PutResult commitConfigurationDataPut(
221             final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
222             final String insert, final String point) {
223         Preconditions.checkNotNull(globalSchema);
224         Preconditions.checkNotNull(path);
225         Preconditions.checkNotNull(payload);
226
227         checkPreconditions();
228
229         final DOMDataReadWriteTransaction newReadWriteTransaction = this.domDataBroker.newReadWriteTransaction();
230         final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null ? Status.OK
231                 : Status.CREATED;
232         final CheckedFuture<Void, TransactionCommitFailedException> future = putDataViaTransaction(
233                 newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema, insert, point);
234         return new PutResult(status, future);
235     }
236
237     /**
238      * <b>PUT configuration data (Mount point)</b>
239      *
240      * Prepare result(status) for PUT operation and PUT data via transaction.
241      * Return wrapped status and future from PUT.
242      *
243      * @param mountPoint
244      *            - mount point for getting transaction for operation and schema
245      *            context for merging parents(if contains list)
246      * @param path
247      *            - path of node
248      * @param payload
249      *            - input data
250      * @param point
251      * @param insert
252      * @return wrapper of status and future of PUT
253      */
254     public PutResult commitMountPointDataPut(
255             final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
256             final String insert, final String point) {
257         Preconditions.checkNotNull(mountPoint);
258         Preconditions.checkNotNull(path);
259         Preconditions.checkNotNull(payload);
260
261         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
262         if (domDataBrokerService.isPresent()) {
263             final DOMDataReadWriteTransaction newReadWriteTransaction = domDataBrokerService.get().newReadWriteTransaction();
264             final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null
265                     ? Status.OK : Status.CREATED;
266             final CheckedFuture<Void, TransactionCommitFailedException> future = putDataViaTransaction(
267                     newReadWriteTransaction, CONFIGURATION, path, payload, mountPoint.getSchemaContext(), insert,
268                     point);
269             return new PutResult(status, future);
270         }
271         final String errMsg = "DOM data broker service isn't available for mount point " + path;
272         LOG.warn(errMsg);
273         throw new RestconfDocumentedException(errMsg);
274     }
275
276     public PATCHStatusContext patchConfigurationDataWithinTransaction(final PATCHContext patchContext) throws Exception {
277         final DOMMountPoint mountPoint = patchContext.getInstanceIdentifierContext().getMountPoint();
278
279         // get new transaction and schema context on server or on mounted device
280         final SchemaContext schemaContext;
281         final DOMDataReadWriteTransaction patchTransaction;
282         if (mountPoint == null) {
283             schemaContext = patchContext.getInstanceIdentifierContext().getSchemaContext();
284             patchTransaction = this.domDataBroker.newReadWriteTransaction();
285         } else {
286             schemaContext = mountPoint.getSchemaContext();
287
288             final Optional<DOMDataBroker> optional = mountPoint.getService(DOMDataBroker.class);
289
290             if (optional.isPresent()) {
291                 patchTransaction = optional.get().newReadWriteTransaction();
292             } else {
293                 // if mount point does not have broker it is not possible to continue and global error is reported
294                 LOG.error("Http PATCH {} has failed - device {} does not support broker service",
295                         patchContext.getPatchId(), mountPoint.getIdentifier());
296                 return new PATCHStatusContext(
297                         patchContext.getPatchId(),
298                         null,
299                         false,
300                         ImmutableList.of(new RestconfError(
301                                 ErrorType.APPLICATION,
302                                 ErrorTag.OPERATION_FAILED,
303                                 "DOM data broker service isn't available for mount point "
304                                         + mountPoint.getIdentifier()))
305                 );
306             }
307         }
308
309         final List<PATCHStatusEntity> editCollection = new ArrayList<>();
310         List<RestconfError> editErrors;
311         boolean withoutError = true;
312
313         for (final PATCHEntity patchEntity : patchContext.getData()) {
314             final PATCHEditOperation operation = PATCHEditOperation.valueOf(patchEntity.getOperation().toUpperCase());
315
316             switch (operation) {
317                 case CREATE:
318                     if (withoutError) {
319                         try {
320                             postDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
321                                     patchEntity.getNode(), schemaContext);
322                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
323                         } catch (final RestconfDocumentedException e) {
324                             LOG.error("Error call http PATCH operation {} on target {}",
325                                     operation,
326                                     patchEntity.getTargetNode().toString());
327
328                             editErrors = new ArrayList<>();
329                             editErrors.addAll(e.getErrors());
330                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
331                             withoutError = false;
332                         }
333                     }
334                     break;
335                 case REPLACE:
336                     if (withoutError) {
337                         try {
338                             putDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
339                                     .getTargetNode(), patchEntity.getNode(), schemaContext);
340                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
341                         } catch (final RestconfDocumentedException e) {
342                             LOG.error("Error call http PATCH operation {} on target {}",
343                                     operation,
344                                     patchEntity.getTargetNode().toString());
345
346                             editErrors = new ArrayList<>();
347                             editErrors.addAll(e.getErrors());
348                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
349                             withoutError = false;
350                         }
351                     }
352                     break;
353                 case DELETE:
354                     if (withoutError) {
355                         try {
356                             deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
357                                     .getTargetNode());
358                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
359                         } catch (final RestconfDocumentedException e) {
360                             LOG.error("Error call http PATCH operation {} on target {}",
361                                     operation,
362                                     patchEntity.getTargetNode().toString());
363
364                             editErrors = new ArrayList<>();
365                             editErrors.addAll(e.getErrors());
366                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
367                             withoutError = false;
368                         }
369                     }
370                     break;
371                 case REMOVE:
372                     if (withoutError) {
373                         try {
374                             deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
375                                     .getTargetNode());
376                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
377                         } catch (final RestconfDocumentedException e) {
378                             LOG.error("Error call http PATCH operation {} on target {}",
379                                     operation,
380                                     patchEntity.getTargetNode().toString());
381
382                             editErrors = new ArrayList<>();
383                             editErrors.addAll(e.getErrors());
384                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
385                             withoutError = false;
386                         }
387                     }
388                     break;
389                 case MERGE:
390                     if (withoutError) {
391                         try {
392                             mergeDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
393                                     patchEntity.getNode(), schemaContext);
394                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
395                         } catch (final RestconfDocumentedException e) {
396                             LOG.error("Error call http PATCH operation {} on target {}",
397                                     operation,
398                                     patchEntity.getTargetNode().toString());
399
400                             editErrors = new ArrayList<>();
401                             editErrors.addAll(e.getErrors());
402                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
403                             withoutError = false;
404                         }
405                     }
406                     break;
407                 default:
408                     LOG.error("Unsupported http PATCH operation {} on target {}",
409                             operation,
410                             patchEntity.getTargetNode().toString());
411                     break;
412             }
413         }
414
415         // if errors then cancel transaction and return error status
416         if (!withoutError) {
417             patchTransaction.cancel();
418             return new PATCHStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
419         }
420
421         // if no errors commit transaction
422         final CountDownLatch waiter = new CountDownLatch(1);
423         final CheckedFuture<Void, TransactionCommitFailedException> future = patchTransaction.submit();
424         final PATCHStatusContextHelper status = new PATCHStatusContextHelper();
425
426         Futures.addCallback(future, new FutureCallback<Void>() {
427             @Override
428             public void onSuccess(@Nullable final Void result) {
429                 status.setStatus(new PATCHStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
430                         true, null));
431                 waiter.countDown();
432             }
433
434             @Override
435             public void onFailure(final Throwable t) {
436                 // if commit failed it is global error
437                 LOG.error("Http PATCH {} transaction commit has failed", patchContext.getPatchId());
438                 status.setStatus(new PATCHStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
439                         false, ImmutableList.of(
440                         new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, t.getMessage()))));
441                 waiter.countDown();
442             }
443         });
444
445         waiter.await();
446         return status.getStatus();
447     }
448
449     // POST configuration
450     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
451             final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
452             final String insert, final String point) {
453         checkPreconditions();
454         return postDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload,
455                 globalSchema, insert, point);
456     }
457
458     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
459             final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
460             final String insert, final String point) {
461         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
462         if (domDataBrokerService.isPresent()) {
463             return postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
464                     payload, mountPoint.getSchemaContext(), insert, point);
465         }
466         final String errMsg = "DOM data broker service isn't available for mount point " + path;
467         LOG.warn(errMsg);
468         throw new RestconfDocumentedException(errMsg);
469     }
470
471     // DELETE configuration
472     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
473             final YangInstanceIdentifier path) {
474         checkPreconditions();
475         return deleteDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path);
476     }
477
478     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
479             final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
480         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
481         if (domDataBrokerService.isPresent()) {
482             return deleteDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path);
483         }
484         final String errMsg = "DOM data broker service isn't available for mount point " + path;
485         LOG.warn(errMsg);
486         throw new RestconfDocumentedException(errMsg);
487     }
488
489     // RPC
490     public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final SchemaPath type, final NormalizedNode<?, ?> input) {
491         checkPreconditions();
492         if (this.rpcService == null) {
493             throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
494         }
495         LOG.trace("Invoke RPC {} with input: {}", type, input);
496         return this.rpcService.invokeRpc(type, input);
497     }
498
499     public void registerToListenDataChanges(final LogicalDatastoreType datastore, final DataChangeScope scope,
500             final ListenerAdapter listener) {
501         checkPreconditions();
502
503         if (listener.isListening()) {
504             return;
505         }
506
507         final YangInstanceIdentifier path = listener.getPath();
508         final ListenerRegistration<DOMDataChangeListener> registration = this.domDataBroker.registerDataChangeListener(
509                 datastore, path, listener, scope);
510
511         listener.setRegistration(registration);
512     }
513
514     private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
515             final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
516         return readDataViaTransaction(transaction, datastore, path, null);
517     }
518
519     private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
520             final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final String withDefa) {
521         LOG.trace("Read {} via Restconf: {}", datastore.name(), path);
522
523         try {
524             final Optional<NormalizedNode<?, ?>> optional = transaction.read(datastore, path).checkedGet();
525             return !optional.isPresent() ? null : withDefa == null ? optional.get() :
526                 prepareDataByParamWithDef(optional.get(), path, withDefa);
527         } catch (ReadFailedException e) {
528             LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
529             throw new RestconfDocumentedException("Error reading data.", e, e.getErrorList());
530         }
531     }
532
533     private NormalizedNode<?, ?> prepareDataByParamWithDef(final NormalizedNode<?, ?> result,
534             final YangInstanceIdentifier path, final String withDefa) {
535         boolean trim;
536         switch (withDefa) {
537             case "trim":
538                 trim = true;
539                 break;
540             case "explicit":
541                 trim = false;
542                 break;
543             default:
544                 throw new RestconfDocumentedException("Bad value used with with-defaults parameter : " + withDefa);
545         }
546
547         final SchemaContext ctx = ControllerContext.getInstance().getGlobalSchema();
548         final DataSchemaContextTree baseSchemaCtxTree = DataSchemaContextTree.from(ctx);
549         final DataSchemaNode baseSchemaNode = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
550         if (result instanceof ContainerNode) {
551             final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder =
552                     Builders.containerBuilder((ContainerSchemaNode) baseSchemaNode);
553             buildCont(builder, (ContainerNode) result, baseSchemaCtxTree, path, trim);
554             return builder.build();
555         } else {
556             final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
557                     Builders.mapEntryBuilder((ListSchemaNode) baseSchemaNode);
558             buildMapEntryBuilder(builder, (MapEntryNode) result, baseSchemaCtxTree, path, trim,
559                     ((ListSchemaNode) baseSchemaNode).getKeyDefinition());
560             return builder.build();
561         }
562     }
563
564     private void buildMapEntryBuilder(final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder,
565             final MapEntryNode result, final DataSchemaContextTree baseSchemaCtxTree,
566             final YangInstanceIdentifier actualPath, final boolean trim, final List<QName> keys) {
567         for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
568             final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
569             final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
570             if (child instanceof ContainerNode) {
571                 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> childBuilder =
572                         Builders.containerBuilder((ContainerSchemaNode) childSchema);
573                 buildCont(childBuilder, (ContainerNode) child, baseSchemaCtxTree, path, trim);
574                 builder.withChild(childBuilder.build());
575             } else if (child instanceof MapNode) {
576                 final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
577                         Builders.mapBuilder((ListSchemaNode) childSchema);
578                 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
579                         ((ListSchemaNode) childSchema).getKeyDefinition());
580                 builder.withChild(childBuilder.build());
581             } else if (child instanceof LeafNode) {
582                 final String defaultVal = ((LeafSchemaNode) childSchema).getDefault();
583                 final String nodeVal = ((LeafNode<String>) child).getValue();
584                 final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
585                         Builders.leafBuilder((LeafSchemaNode) childSchema);
586                 if (keys.contains(child.getNodeType())) {
587                     leafBuilder.withValue(((LeafNode) child).getValue());
588                     builder.withChild(leafBuilder.build());
589                 } else {
590                     if (trim) {
591                         if (defaultVal == null || !defaultVal.equals(nodeVal)) {
592                             leafBuilder.withValue(((LeafNode) child).getValue());
593                             builder.withChild(leafBuilder.build());
594                         }
595                     } else {
596                         if (defaultVal != null && defaultVal.equals(nodeVal)) {
597                             leafBuilder.withValue(((LeafNode) child).getValue());
598                             builder.withChild(leafBuilder.build());
599                         }
600                     }
601                 }
602             }
603         }
604     }
605
606     private void buildList(final CollectionNodeBuilder<MapEntryNode, MapNode> builder, final MapNode result,
607             final DataSchemaContextTree baseSchemaCtxTree, final YangInstanceIdentifier path, final boolean trim,
608             final List<QName> keys) {
609         for (final MapEntryNode mapEntryNode : result.getValue()) {
610             final YangInstanceIdentifier actualNode = path.node(mapEntryNode.getIdentifier());
611             final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(actualNode).getDataSchemaNode();
612             final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
613                     Builders.mapEntryBuilder((ListSchemaNode) childSchema);
614             buildMapEntryBuilder(mapEntryBuilder, mapEntryNode, baseSchemaCtxTree, actualNode, trim, keys);
615             builder.withChild(mapEntryBuilder.build());
616         }
617     }
618
619     private void buildCont(final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder,
620             final ContainerNode result, final DataSchemaContextTree baseSchemaCtxTree,
621             final YangInstanceIdentifier actualPath, final boolean trim) {
622         for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
623             final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
624             final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
625             if(child instanceof ContainerNode){
626                 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builderChild =
627                         Builders.containerBuilder((ContainerSchemaNode) childSchema);
628                 buildCont(builderChild, result, baseSchemaCtxTree, actualPath, trim);
629                 builder.withChild(builderChild.build());
630             } else if (child instanceof MapNode) {
631                 final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
632                         Builders.mapBuilder((ListSchemaNode) childSchema);
633                 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
634                         ((ListSchemaNode) childSchema).getKeyDefinition());
635                 builder.withChild(childBuilder.build());
636             } else if (child instanceof LeafNode) {
637                 final String defaultVal = ((LeafSchemaNode) childSchema).getDefault();
638                 final String nodeVal = ((LeafNode<String>) child).getValue();
639                 final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
640                         Builders.leafBuilder((LeafSchemaNode) childSchema);
641                 if (trim) {
642                     if (defaultVal == null || !defaultVal.equals(nodeVal)) {
643                         leafBuilder.withValue(((LeafNode) child).getValue());
644                         builder.withChild(leafBuilder.build());
645                     }
646                 } else {
647                     if (defaultVal != null && defaultVal.equals(nodeVal)) {
648                         leafBuilder.withValue(((LeafNode) child).getValue());
649                         builder.withChild(leafBuilder.build());
650                     }
651                 }
652             }
653         }
654     }
655
656     /**
657      * POST data and submit transaction {@link DOMDataReadWriteTransaction}
658      */
659     private CheckedFuture<Void, TransactionCommitFailedException> postDataViaTransaction(
660             final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
661             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
662             final String insert, final String point) {
663         LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
664         postData(rWTransaction, datastore, path, payload, schemaContext, insert, point);
665         return rWTransaction.submit();
666     }
667
668     /**
669      * POST data and do NOT submit transaction {@link DOMDataReadWriteTransaction}
670      */
671     private void postDataWithinTransaction(
672             final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
673             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
674         LOG.trace("POST {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
675         postData(rWTransaction, datastore, path, payload, schemaContext, null, null);
676     }
677
678     private void postData(final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
679                           final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
680             final SchemaContext schemaContext, final String insert, final String point) {
681         if (insert == null) {
682             makeNormalPost(rWTransaction, datastore, path, payload, schemaContext);
683         } else {
684             final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
685             checkItemDoesNotExists(rWTransaction, datastore, path);
686             switch (insert) {
687                 case "first":
688                     if(schemaNode instanceof ListSchemaNode){
689                         final OrderedMapNode readList =
690                                 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
691                         if (readList == null || readList.getValue().isEmpty()) {
692                             simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
693                         } else {
694                             rWTransaction.delete(datastore, path.getParent().getParent());
695                             simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
696                             makeNormalPost(rWTransaction, datastore, path.getParent().getParent(), readList,
697                                     schemaContext);
698                         }
699                     } else {
700                         final OrderedLeafSetNode readLeafList =
701                                 (OrderedLeafSetNode) readConfigurationData(path.getParent());
702                         if (readLeafList == null || readLeafList.getValue().isEmpty()) {
703                             simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
704                         } else {
705                             rWTransaction.delete(datastore, path.getParent());
706                             simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
707                             makeNormalPost(rWTransaction, datastore, path.getParent().getParent(), readLeafList,
708                                     schemaContext);
709                         }
710                     }
711                     break;
712                 case "last":
713                     simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
714                     break;
715                 case "before":
716                     if(schemaNode instanceof ListSchemaNode){
717                         final OrderedMapNode readList =
718                                 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
719                         if (readList == null || readList.getValue().isEmpty()) {
720                             simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
721                         } else {
722                             insertWithPointListPost(rWTransaction, datastore, path, payload, schemaContext, point,
723                                     readList,
724                                     true);
725                         }
726                     } else {
727                         final OrderedLeafSetNode<?> readLeafList =
728                                 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
729                         if (readLeafList == null || readLeafList.getValue().isEmpty()) {
730                             simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
731                         } else {
732                             insertWithPointLeafListPost(rWTransaction, datastore, path, payload, schemaContext, point,
733                                     readLeafList, true);
734                         }
735                     }
736                     break;
737                 case "after":
738                     if (schemaNode instanceof ListSchemaNode) {
739                         final OrderedMapNode readList =
740                                 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
741                         if (readList == null || readList.getValue().isEmpty()) {
742                             simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
743                         } else {
744                             insertWithPointListPost(rWTransaction, datastore, path, payload, schemaContext, point,
745                                     readList,
746                                     false);
747                         }
748                     } else {
749                         final OrderedLeafSetNode<?> readLeafList =
750                                 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
751                         if (readLeafList == null || readLeafList.getValue().isEmpty()) {
752                             simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
753                         } else {
754                             insertWithPointLeafListPost(rWTransaction, datastore, path, payload, schemaContext, point,
755                                     readLeafList, false);
756                         }
757                     }
758                     break;
759                 default:
760                     throw new RestconfDocumentedException(
761                             "Used bad value of insert parameter. Possible values are first, last, before or after, "
762                                     + "but was: " + insert);
763             }
764         }
765     }
766
767     private void insertWithPointLeafListPost(final DOMDataReadWriteTransaction rWTransaction,
768             final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
769             final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
770             final boolean before) {
771         rWTransaction.delete(datastore, path.getParent().getParent());
772         final InstanceIdentifierContext<?> instanceIdentifier =
773                 ControllerContext.getInstance().toInstanceIdentifier(point);
774         int p = 0;
775         for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
776             if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
777                 break;
778             }
779             p++;
780         }
781         if (!before) {
782             p++;
783         }
784         int h = 0;
785         final NormalizedNode<?, ?> emptySubtree =
786                 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
787         rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
788         for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
789             if (h == p) {
790                 checkItemDoesNotExists(rWTransaction, datastore, path);
791                 simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
792             }
793             final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
794             checkItemDoesNotExists(rWTransaction, datastore, childPath);
795             rWTransaction.put(datastore, childPath, nodeChild);
796             h++;
797         }
798     }
799
800     private void insertWithPointListPost(final DOMDataReadWriteTransaction rWTransaction,
801             final LogicalDatastoreType datastore,
802             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
803             final String point, final MapNode readList, final boolean before) {
804         rWTransaction.delete(datastore, path.getParent().getParent());
805         final InstanceIdentifierContext<?> instanceIdentifier =
806                 ControllerContext.getInstance().toInstanceIdentifier(point);
807         int p = 0;
808         for (final MapEntryNode mapEntryNode : readList.getValue()) {
809             if (mapEntryNode.getIdentifier()
810                     .equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
811                 break;
812             }
813             p++;
814         }
815         if (!before) {
816             p++;
817         }
818         int h = 0;
819         final NormalizedNode<?, ?> emptySubtree =
820                 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
821         rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
822         for (final MapEntryNode mapEntryNode : readList.getValue()) {
823             if (h == p) {
824                 checkItemDoesNotExists(rWTransaction, datastore, path);
825                 simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
826             }
827             final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
828             checkItemDoesNotExists(rWTransaction, datastore, childPath);
829             rWTransaction.put(datastore, childPath, mapEntryNode);
830             h++;
831         }
832     }
833
834     private DataSchemaNode checkListAndOrderedType(final SchemaContext ctx,
835             final YangInstanceIdentifier path) {
836         final YangInstanceIdentifier parent = path.getParent();
837         final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).getChild(parent);
838         final DataSchemaNode dataSchemaNode = node.getDataSchemaNode();
839
840         if (dataSchemaNode instanceof ListSchemaNode) {
841             if(!((ListSchemaNode) dataSchemaNode).isUserOrdered()) {
842                 throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list.");
843             }
844             return dataSchemaNode;
845         }
846         if (dataSchemaNode instanceof LeafListSchemaNode) {
847             if(!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) {
848                 throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user leaf-list.");
849             }
850             return dataSchemaNode;
851         }
852         throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list");
853     }
854
855     private void makeNormalPost(final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
856             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
857         if (payload instanceof MapNode) {
858             final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
859             rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
860             ensureParentsByMerge(datastore, path, rWTransaction, schemaContext);
861             for (final MapEntryNode child : ((MapNode) payload).getValue()) {
862                 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
863                 checkItemDoesNotExists(rWTransaction, datastore, childPath);
864                 rWTransaction.put(datastore, childPath, child);
865             }
866         } else if (payload instanceof LeafSetNode) {
867             final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
868             rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
869             ensureParentsByMerge(datastore, path, rWTransaction, schemaContext);
870             for (final LeafSetEntryNode<?> child : ((LeafSetNode<?>) payload).getValue()) {
871                 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
872                 checkItemDoesNotExists(rWTransaction, datastore, childPath);
873                 rWTransaction.put(datastore, childPath, child);
874             }
875         } else {
876             simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
877         }
878     }
879
880     private void simplePostPut(final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
881             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
882         checkItemDoesNotExists(rWTransaction, datastore, path);
883         ensureParentsByMerge(datastore, path, rWTransaction, schemaContext);
884         rWTransaction.put(datastore, path, payload);
885     }
886
887     private boolean doesItemExist(final DOMDataReadWriteTransaction rWTransaction,
888             final LogicalDatastoreType store, final YangInstanceIdentifier path) {
889         try {
890             return rWTransaction.exists(store, path).checkedGet();
891         } catch (ReadFailedException e) {
892             rWTransaction.cancel();
893             throw new RestconfDocumentedException("Could not determine the existence of path " + path,
894                     e, e.getErrorList());
895         }
896     }
897
898     /**
899      * Check if item already exists. Throws error if it does NOT already exist.
900      * @param rWTransaction Current transaction
901      * @param store Used datastore
902      * @param path Path to item to verify its existence
903      */
904     private void checkItemExists(final DOMDataReadWriteTransaction rWTransaction,
905                                  final LogicalDatastoreType store, final YangInstanceIdentifier path) {
906         if (!doesItemExist(rWTransaction, store, path)) {
907             final String errMsg = "Operation via Restconf was not executed because data does not exist";
908             LOG.trace("{}:{}", errMsg, path);
909             rWTransaction.cancel();
910             throw new RestconfDocumentedException("Data does not exist for path: " + path, ErrorType.PROTOCOL,
911                     ErrorTag.DATA_MISSING);
912         }
913     }
914
915     /**
916      * Check if item does NOT already exist. Throws error if it already exists.
917      * @param rWTransaction Current transaction
918      * @param store Used datastore
919      * @param path Path to item to verify its existence
920      */
921     private void checkItemDoesNotExists(final DOMDataReadWriteTransaction rWTransaction,
922                                         final LogicalDatastoreType store, final YangInstanceIdentifier path) {
923         if (doesItemExist(rWTransaction, store, path)) {
924             final String errMsg = "Operation via Restconf was not executed because data already exists";
925             LOG.trace("{}:{}", errMsg, path);
926             rWTransaction.cancel();
927             throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
928                     ErrorTag.DATA_EXISTS);
929         }
930     }
931
932     /**
933      * PUT data and submit {@link DOMDataReadWriteTransaction}
934      *
935      * @param point
936      * @param insert
937      */
938     private CheckedFuture<Void, TransactionCommitFailedException> putDataViaTransaction(
939             final DOMDataReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
940             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
941             final String insert, final String point) {
942         LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload);
943         putData(readWriteTransaction, datastore, path, payload, schemaContext, insert, point);
944         return readWriteTransaction.submit();
945     }
946
947     /**
948      * PUT data and do NOT submit {@link DOMDataReadWriteTransaction}
949      *
950      * @param insert
951      * @param point
952      */
953     private void putDataWithinTransaction(
954             final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
955             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
956         LOG.trace("Put {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
957         putData(writeTransaction, datastore, path, payload, schemaContext, null, null);
958     }
959
960     // FIXME: This is doing correct put for container and list children, not sure if this will work for choice case
961     private void putData(final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
962             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
963             final String insert, final String point) {
964         if (insert == null) {
965             makePut(rWTransaction, datastore, path, payload, schemaContext);
966         } else {
967             final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
968             checkItemDoesNotExists(rWTransaction, datastore, path);
969             switch (insert) {
970                 case "first":
971                     if (schemaNode instanceof ListSchemaNode) {
972                         final OrderedMapNode readList =
973                                 (OrderedMapNode) this.readConfigurationData(path.getParent());
974                         if (readList == null || readList.getValue().isEmpty()) {
975                             simplePut(datastore, path, rWTransaction, schemaContext, payload);
976                         } else {
977                             rWTransaction.delete(datastore, path.getParent());
978                             simplePut(datastore, path, rWTransaction, schemaContext, payload);
979                             makePut(rWTransaction, datastore, path.getParent(), readList, schemaContext);
980                         }
981                     } else {
982                         final OrderedLeafSetNode<?> readLeafList =
983                                 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
984                         if (readLeafList == null || readLeafList.getValue().isEmpty()) {
985                             simplePut(datastore, path, rWTransaction, schemaContext, payload);
986                         } else {
987                             rWTransaction.delete(datastore, path.getParent());
988                             simplePut(datastore, path, rWTransaction, schemaContext, payload);
989                             makePut(rWTransaction, datastore, path.getParent(), readLeafList,
990                                     schemaContext);
991                         }
992                     }
993                     break;
994                 case "last":
995                     simplePut(datastore, path, rWTransaction, schemaContext, payload);
996                     break;
997                 case "before":
998                     if (schemaNode instanceof ListSchemaNode) {
999                         final OrderedMapNode readList =
1000                                 (OrderedMapNode) this.readConfigurationData(path.getParent());
1001                         if (readList == null || readList.getValue().isEmpty()) {
1002                             simplePut(datastore, path, rWTransaction, schemaContext, payload);
1003                         } else {
1004                             insertWithPointListPut(rWTransaction, datastore, path, payload, schemaContext, point,
1005                                     readList, true);
1006                         }
1007                     } else {
1008                         final OrderedLeafSetNode<?> readLeafList =
1009                                 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1010                         if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1011                             simplePut(datastore, path, rWTransaction, schemaContext, payload);
1012                         } else {
1013                             insertWithPointLeafListPut(rWTransaction, datastore, path, payload, schemaContext, point,
1014                                     readLeafList, true);
1015                         }
1016                     }
1017                     break;
1018                 case "after":
1019                     if (schemaNode instanceof ListSchemaNode) {
1020                         final OrderedMapNode readList =
1021                                 (OrderedMapNode) this.readConfigurationData(path.getParent());
1022                         if (readList == null || readList.getValue().isEmpty()) {
1023                             simplePut(datastore, path, rWTransaction, schemaContext, payload);
1024                         } else {
1025                             insertWithPointListPut(rWTransaction, datastore, path, payload, schemaContext, point,
1026                                     readList, false);
1027                         }
1028                     } else {
1029                         final OrderedLeafSetNode<?> readLeafList =
1030                                 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1031                         if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1032                             simplePut(datastore, path, rWTransaction, schemaContext, payload);
1033                         } else {
1034                             insertWithPointLeafListPut(rWTransaction, datastore, path, payload, schemaContext, point,
1035                                     readLeafList, false);
1036                         }
1037                     }
1038                     break;
1039                 default:
1040                     throw new RestconfDocumentedException(
1041                             "Used bad value of insert parameter. Possible values are first, last, before or after, "
1042                                     + "but was: " + insert);
1043             }
1044         }
1045     }
1046
1047     private void insertWithPointLeafListPut(final DOMDataReadWriteTransaction rWTransaction,
1048             final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
1049             final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
1050             final boolean before) {
1051         rWTransaction.delete(datastore, path.getParent());
1052         final InstanceIdentifierContext<?> instanceIdentifier =
1053                 ControllerContext.getInstance().toInstanceIdentifier(point);
1054         int p = 0;
1055         for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
1056             if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1057                 break;
1058             }
1059             p++;
1060         }
1061         if (!before) {
1062             p++;
1063         }
1064         int h = 0;
1065         final NormalizedNode<?, ?> emptySubtree =
1066                 ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1067         rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1068         for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
1069             if (h == p) {
1070                 simplePut(datastore, path, rWTransaction, schemaContext, payload);
1071             }
1072             final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
1073             rWTransaction.put(datastore, childPath, nodeChild);
1074             h++;
1075         }
1076     }
1077
1078     private void insertWithPointListPut(final DOMDataReadWriteTransaction rWTransaction,
1079             final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
1080             final SchemaContext schemaContext, final String point, final OrderedMapNode readList,
1081             final boolean before) {
1082         rWTransaction.delete(datastore, path.getParent());
1083         final InstanceIdentifierContext<?> instanceIdentifier =
1084                 ControllerContext.getInstance().toInstanceIdentifier(point);
1085         int p = 0;
1086         for (final MapEntryNode mapEntryNode : readList.getValue()) {
1087             if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1088                 break;
1089             }
1090             p++;
1091         }
1092         if (!before) {
1093             p++;
1094         }
1095         int h = 0;
1096         final NormalizedNode<?, ?> emptySubtree =
1097                 ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1098         rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1099         for (final MapEntryNode mapEntryNode : readList.getValue()) {
1100             if (h == p) {
1101                 simplePut(datastore, path, rWTransaction, schemaContext, payload);
1102             }
1103             final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
1104             rWTransaction.put(datastore, childPath, mapEntryNode);
1105             h++;
1106         }
1107     }
1108
1109     private void makePut(final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
1110             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
1111         if (payload instanceof MapNode) {
1112             final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
1113             writeTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1114             ensureParentsByMerge(datastore, path, writeTransaction, schemaContext);
1115             for (final MapEntryNode child : ((MapNode) payload).getValue()) {
1116                 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
1117                 writeTransaction.put(datastore, childPath, child);
1118             }
1119         } else {
1120             simplePut(datastore, path, writeTransaction, schemaContext, payload);
1121         }
1122     }
1123
1124     private void simplePut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
1125             final DOMDataReadWriteTransaction writeTransaction, final SchemaContext schemaContext,
1126             final NormalizedNode<?, ?> payload) {
1127         ensureParentsByMerge(datastore, path, writeTransaction, schemaContext);
1128         writeTransaction.put(datastore, path, payload);
1129     }
1130
1131     private CheckedFuture<Void, TransactionCommitFailedException> deleteDataViaTransaction(
1132             final DOMDataReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
1133             final YangInstanceIdentifier path) {
1134         LOG.trace("Delete {} via Restconf: {}", datastore.name(), path);
1135         checkItemExists(readWriteTransaction, datastore, path);
1136         readWriteTransaction.delete(datastore, path);
1137         return readWriteTransaction.submit();
1138     }
1139
1140     private void deleteDataWithinTransaction(
1141             final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
1142             final YangInstanceIdentifier path) {
1143         LOG.trace("Delete {} within Restconf PATCH: {}", datastore.name(), path);
1144         writeTransaction.delete(datastore, path);
1145     }
1146
1147     private void mergeDataWithinTransaction(
1148             final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
1149             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
1150         LOG.trace("Merge {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
1151         ensureParentsByMerge(datastore, path, writeTransaction, schemaContext);
1152
1153         // merging is necessary only for lists otherwise we can call put method
1154         if (payload instanceof MapNode) {
1155             writeTransaction.merge(datastore, path, payload);
1156         } else {
1157             writeTransaction.put(datastore, path, payload);
1158         }
1159     }
1160
1161     public void setDomDataBroker(final DOMDataBroker domDataBroker) {
1162         this.domDataBroker = domDataBroker;
1163     }
1164
1165     public void registerToListenNotification(final NotificationListenerAdapter listener) {
1166         checkPreconditions();
1167
1168         if (listener.isListening()) {
1169             return;
1170         }
1171
1172         final SchemaPath path = listener.getSchemaPath();
1173         final ListenerRegistration<DOMNotificationListener> registration = this.domNotification
1174                 .registerNotificationListener(listener, path);
1175
1176         listener.setRegistration(registration);
1177     }
1178
1179     private final class PATCHStatusContextHelper {
1180         PATCHStatusContext status;
1181
1182         public PATCHStatusContext getStatus() {
1183             return this.status;
1184         }
1185
1186         public void setStatus(final PATCHStatusContext status) {
1187             this.status = status;
1188         }
1189     }
1190
1191     private void ensureParentsByMerge(final LogicalDatastoreType store, final YangInstanceIdentifier normalizedPath,
1192             final DOMDataReadWriteTransaction rwTx, final SchemaContext schemaContext) {
1193         final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>();
1194         YangInstanceIdentifier rootNormalizedPath = null;
1195
1196         final Iterator<PathArgument> it = normalizedPath.getPathArguments().iterator();
1197
1198         while (it.hasNext()) {
1199             final PathArgument pathArgument = it.next();
1200             if (rootNormalizedPath == null) {
1201                 rootNormalizedPath = YangInstanceIdentifier.create(pathArgument);
1202             }
1203
1204             if (it.hasNext()) {
1205                 normalizedPathWithoutChildArgs.add(pathArgument);
1206             }
1207         }
1208
1209         if (normalizedPathWithoutChildArgs.isEmpty()) {
1210             return;
1211         }
1212
1213         Preconditions.checkArgument(rootNormalizedPath != null, "Empty path received");
1214
1215         final NormalizedNode<?, ?> parentStructure = ImmutableNodes.fromInstanceId(schemaContext,
1216                 YangInstanceIdentifier.create(normalizedPathWithoutChildArgs));
1217         rwTx.merge(store, rootNormalizedPath, parentStructure);
1218     }
1219 }