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