7c2829dab189625770616f2803e0741aadd04b6a
[mdsal.git] / binding / mdsal-binding-dom-adapter / src / main / java / org / opendaylight / mdsal / binding / dom / adapter / AbstractWriteTransaction.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.mdsal.binding.dom.adapter;
9
10 import com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import com.google.common.util.concurrent.CheckedFuture;
13 import java.util.Map.Entry;
14 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
15 import org.opendaylight.mdsal.common.api.TransactionCommitFailedException;
16 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
17 import org.opendaylight.yangtools.yang.binding.DataObject;
18 import org.opendaylight.yangtools.yang.binding.Identifiable;
19 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
21 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
22
23 /**
24  * Abstract Base Transaction for transactions which are backed by
25  * {@link DOMDataTreeWriteTransaction}.
26  */
27 public abstract class AbstractWriteTransaction<T extends DOMDataTreeWriteTransaction> extends
28         AbstractForwardedTransaction<T> {
29
30     protected AbstractWriteTransaction(final T delegate, final BindingToNormalizedNodeCodec codec) {
31         super(delegate, codec);
32     }
33
34     public final <U extends DataObject> void put(final LogicalDatastoreType store,
35             final InstanceIdentifier<U> path, final U data, final boolean createParents) {
36         Preconditions.checkArgument(!path.isWildcarded(), "Cannot put data into wildcarded path %s", path);
37
38         final Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalized = getCodec().toNormalizedNode(path, data);
39         if (createParents) {
40             ensureParentsByMerge(store, normalized.getKey(), path);
41         } else {
42             ensureListParentIfNeeded(store,path,normalized);
43         }
44
45         getDelegate().put(store, normalized.getKey(), normalized.getValue());
46     }
47
48     public final <U extends DataObject> void merge(final LogicalDatastoreType store,
49             final InstanceIdentifier<U> path, final U data,final boolean createParents) {
50         Preconditions.checkArgument(!path.isWildcarded(), "Cannot merge data into wildcarded path %s", path);
51
52         final Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalized = getCodec().toNormalizedNode(path, data);
53         if (createParents) {
54             ensureParentsByMerge(store, normalized.getKey(), path);
55         } else {
56             ensureListParentIfNeeded(store,path,normalized);
57         }
58
59         getDelegate().merge(store, normalized.getKey(), normalized.getValue());
60     }
61
62     /**
63      * Ensures list parent if item is list, otherwise noop.
64      *
65      * <p>
66      * One of properties of binding specification is that it is imposible
67      * to represent list as a whole and thus it is impossible to write
68      * empty variation of MapNode without creating parent node, with
69      * empty list.
70      *
71      * <p>
72      * This actually makes writes such as
73      * <pre>
74      * put("Nodes", new NodesBuilder().build());
75      * put("Nodes/Node[key]", new NodeBuilder().setKey("key").build());
76      * </pre>
77      * To result in three DOM operations:
78      * <pre>
79      * put("/nodes",domNodes);
80      * merge("/nodes/node",domNodeList);
81      * put("/nodes/node/node[key]",domNode);
82      * </pre>
83      *
84      * <p>
85      * In order to allow that to be inserted if necessary, if we know
86      * item is list item, we will try to merge empty MapNode or OrderedNodeMap
87      * to ensure list exists.
88      *
89      * @param store Data Store type
90      * @param path Path to data (Binding Aware)
91      * @param normalized Normalized version of data to be written
92      */
93     private void ensureListParentIfNeeded(final LogicalDatastoreType store, final InstanceIdentifier<?> path,
94             final Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalized) {
95         if (Identifiable.class.isAssignableFrom(path.getTargetType())) {
96             final YangInstanceIdentifier parentMapPath = normalized.getKey().getParent();
97             Preconditions.checkArgument(parentMapPath != null, "Map path %s does not have a parent", path);
98
99             final NormalizedNode<?, ?> emptyParent = getCodec().getDefaultNodeFor(parentMapPath);
100             getDelegate().merge(store, parentMapPath, emptyParent);
101         }
102     }
103
104     /**
105      * Use {@link YangInstanceIdentifier#getParent()} instead.
106      */
107     @Deprecated
108     protected static Optional<YangInstanceIdentifier> getParent(final YangInstanceIdentifier child) {
109         return Optional.fromNullable(child.getParent());
110     }
111
112     /**
113      * Subclasses of this class are required to implement creation of parent nodes based on
114      * behaviour of their underlying transaction.
115      *
116      * @param store an instance of LogicalDatastoreType
117      * @param domPath an instance of YangInstanceIdentifier
118      * @param path an instance of InstanceIdentifier
119      */
120     protected final void ensureParentsByMerge(final LogicalDatastoreType store, final YangInstanceIdentifier domPath,
121             final InstanceIdentifier<?> path) {
122         final YangInstanceIdentifier parentPath = domPath.getParent();
123         if (parentPath != null) {
124             final NormalizedNode<?, ?> parentNode = getCodec().instanceIdentifierToNode(parentPath);
125             getDelegate().merge(store, YangInstanceIdentifier.create(parentNode.getIdentifier()), parentNode);
126         }
127     }
128
129     protected final void doDelete(final LogicalDatastoreType store,
130             final InstanceIdentifier<?> path) {
131         Preconditions.checkArgument(!path.isWildcarded(), "Cannot delete wildcarded path %s", path);
132
133         final YangInstanceIdentifier normalized = getCodec().toYangInstanceIdentifierBlocking(path);
134         getDelegate().delete(store, normalized);
135     }
136
137     protected final CheckedFuture<Void,TransactionCommitFailedException> doSubmit() {
138         return getDelegate().submit();
139     }
140
141     protected final boolean doCancel() {
142         return getDelegate().cancel();
143     }
144
145 }