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