BUG-509: remove unnecessary Version objects
[controller.git] / opendaylight / md-sal / sal-dom-broker / src / main / java / org / opendaylight / controller / md / sal / dom / store / impl / tree / data / NormalizedNodeContainerModificationStrategy.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.dom.store.impl.tree.data;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11
12 import java.util.Map;
13
14 import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
15 import org.opendaylight.controller.md.sal.dom.store.impl.tree.ModificationType;
16 import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreUtils;
17 import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.DataNodeContainerModificationStrategy.ListEntryModificationStrategy;
18 import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.ValueNodeModificationStrategy.LeafSetEntryModificationStrategy;
19 import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.MutableTreeNode;
20 import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.TreeNode;
21 import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.TreeNodeFactory;
22 import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.Version;
23 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
24 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
25 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
26 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeWithValue;
27 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
28 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
29 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
32 import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
34 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
35 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
36 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableChoiceNodeBuilder;
37 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
38 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
39 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedLeafSetNodeBuilder;
40 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedMapNodeBuilder;
41 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
42 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
43 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
45 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
46
47 import com.google.common.base.Optional;
48 import com.google.common.collect.ImmutableMap;
49 import com.google.common.collect.Iterables;
50
51 abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareApplyOperation {
52
53     private final Class<? extends NormalizedNode<?, ?>> nodeClass;
54
55     protected NormalizedNodeContainerModificationStrategy(final Class<? extends NormalizedNode<?, ?>> nodeClass) {
56         this.nodeClass = nodeClass;
57     }
58
59     @Override
60     public void verifyStructure(final ModifiedNode modification) throws IllegalArgumentException {
61         if (modification.getType() == ModificationType.WRITE) {
62
63         }
64         for (ModifiedNode childModification : modification.getChildren()) {
65             resolveChildOperation(childModification.getIdentifier()).verifyStructure(childModification);
66         }
67     }
68
69     @Override
70     protected void checkWriteApplicable(final InstanceIdentifier path, final NodeModification modification,
71             final Optional<TreeNode> current) throws DataPreconditionFailedException {
72         // FIXME: Implement proper write check for replacement of node container
73         //        prerequisite is to have transaction chain available for clients
74         //        otherwise this will break chained writes to same node.
75     }
76
77     @SuppressWarnings("rawtypes")
78     @Override
79     protected void verifyWrittenStructure(final NormalizedNode<?, ?> writtenValue) {
80         checkArgument(nodeClass.isInstance(writtenValue), "Node should must be of type %s", nodeClass);
81         checkArgument(writtenValue instanceof NormalizedNodeContainer);
82
83         NormalizedNodeContainer container = (NormalizedNodeContainer) writtenValue;
84         for (Object child : container.getValue()) {
85             checkArgument(child instanceof NormalizedNode);
86
87             /*
88              * FIXME: fail-fast semantics:
89              *
90              * We can validate the data structure here, aborting the commit
91              * before it ever progresses to being committed.
92              */
93         }
94     }
95
96     @Override
97     protected TreeNode applyWrite(final ModifiedNode modification,
98             final Optional<TreeNode> currentMeta, final Version version) {
99         final NormalizedNode<?, ?> newValue = modification.getWrittenValue();
100         final TreeNode newValueMeta = TreeNodeFactory.createTreeNode(newValue, version);
101
102         if (Iterables.isEmpty(modification.getChildren())) {
103             return newValueMeta;
104         }
105
106         /*
107          * This is where things get interesting. The user has performed a write and
108          * then she applied some more modifications to it. So we need to make sense
109          * of that an apply the operations on top of the written value. We could have
110          * done it during the write, but this operation is potentially expensive, so
111          * we have left it out of the fast path.
112          *
113          * As it turns out, once we materialize the written data, we can share the
114          * code path with the subtree change. So let's create an unsealed TreeNode
115          * and run the common parts on it -- which end with the node being sealed.
116          */
117         final MutableTreeNode mutable = newValueMeta.mutable();
118         mutable.setSubtreeVersion(version);
119
120         @SuppressWarnings("rawtypes")
121         final NormalizedNodeContainerBuilder dataBuilder = createBuilder(newValue);
122
123         return mutateChildren(mutable, dataBuilder, version, modification.getChildren());
124     }
125
126     @SuppressWarnings({ "rawtypes", "unchecked" })
127     private TreeNode mutateChildren(final MutableTreeNode meta, final NormalizedNodeContainerBuilder data,
128             final Version nodeVersion, final Iterable<ModifiedNode> modifications) {
129
130         for (ModifiedNode mod : modifications) {
131             final PathArgument id = mod.getIdentifier();
132             final Optional<TreeNode> cm = meta.getChild(id);
133
134             Optional<TreeNode> result = resolveChildOperation(id).apply(mod, cm, nodeVersion);
135             if (result.isPresent()) {
136                 final TreeNode tn = result.get();
137                 meta.addChild(tn);
138                 data.addChild(tn.getData());
139             } else {
140                 meta.removeChild(id);
141                 data.removeChild(id);
142             }
143         }
144
145         meta.setData(data.build());
146         return meta.seal();
147     }
148
149     @Override
150     protected TreeNode applyMerge(final ModifiedNode modification, final TreeNode currentMeta,
151             final Version version) {
152         // For Node Containers - merge is same as subtree change - we only replace children.
153         return applySubtreeChange(modification, currentMeta, version);
154     }
155
156     @Override
157     public TreeNode applySubtreeChange(final ModifiedNode modification,
158             final TreeNode currentMeta, final Version version) {
159         final MutableTreeNode newMeta = currentMeta.mutable();
160         newMeta.setSubtreeVersion(version);
161
162         @SuppressWarnings("rawtypes")
163         NormalizedNodeContainerBuilder dataBuilder = createBuilder(currentMeta.getData());
164
165         return mutateChildren(newMeta, dataBuilder, version, modification.getChildren());
166     }
167
168     @Override
169     protected void checkSubtreeModificationApplicable(final InstanceIdentifier path, final NodeModification modification,
170             final Optional<TreeNode> current) throws DataPreconditionFailedException {
171         checkDataPrecondition(path, current.isPresent(), "Node was deleted by other transaction.");
172         checkChildPreconditions(path, modification, current);
173     }
174
175     private void checkChildPreconditions(final InstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataPreconditionFailedException {
176         final TreeNode currentMeta = current.get();
177         for (NodeModification childMod : modification.getChildren()) {
178             final PathArgument childId = childMod.getIdentifier();
179             final Optional<TreeNode> childMeta = currentMeta.getChild(childId);
180
181             InstanceIdentifier childPath = StoreUtils.append(path, childId);
182             resolveChildOperation(childId).checkApplicable(childPath, childMod, childMeta);
183         }
184     }
185
186     @Override
187     protected void checkMergeApplicable(final InstanceIdentifier path, final NodeModification modification,
188             final Optional<TreeNode> current) throws DataPreconditionFailedException {
189         if(current.isPresent()) {
190             checkChildPreconditions(path, modification,current);
191         }
192     }
193
194     @SuppressWarnings("rawtypes")
195     protected abstract NormalizedNodeContainerBuilder createBuilder(NormalizedNode<?, ?> original);
196
197     public static class ChoiceModificationStrategy extends NormalizedNodeContainerModificationStrategy {
198
199         private final Map<PathArgument, ModificationApplyOperation> childNodes;
200
201         public ChoiceModificationStrategy(final ChoiceNode schemaNode) {
202             super(org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode.class);
203             ImmutableMap.Builder<PathArgument, ModificationApplyOperation> child = ImmutableMap.builder();
204
205             for (ChoiceCaseNode caze : schemaNode.getCases()) {
206                 for (DataSchemaNode cazeChild : caze.getChildNodes()) {
207                     SchemaAwareApplyOperation childNode = from(cazeChild);
208                     child.put(new NodeIdentifier(cazeChild.getQName()), childNode);
209                 }
210             }
211             childNodes = child.build();
212         }
213
214         @Override
215         public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
216             return Optional.fromNullable(childNodes.get(child));
217         }
218
219         @Override
220         @SuppressWarnings("rawtypes")
221         protected DataContainerNodeBuilder createBuilder(final NormalizedNode<?, ?> original) {
222             checkArgument(original instanceof org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode);
223             return ImmutableChoiceNodeBuilder.create((org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode) original);
224         }
225     }
226
227     public static class OrderedLeafSetModificationStrategy extends NormalizedNodeContainerModificationStrategy {
228
229         private final Optional<ModificationApplyOperation> entryStrategy;
230
231         @SuppressWarnings({ "unchecked", "rawtypes" })
232         protected OrderedLeafSetModificationStrategy(final LeafListSchemaNode schema) {
233             super((Class) LeafSetNode.class);
234             entryStrategy = Optional.<ModificationApplyOperation> of(new LeafSetEntryModificationStrategy(schema));
235         }
236
237         @SuppressWarnings("rawtypes")
238         @Override
239         protected NormalizedNodeContainerBuilder createBuilder(final NormalizedNode<?, ?> original) {
240             checkArgument(original instanceof OrderedLeafSetNode<?>);
241             return ImmutableOrderedLeafSetNodeBuilder.create((OrderedLeafSetNode<?>) original);
242         }
243
244         @Override
245         public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
246             if (identifier instanceof NodeWithValue) {
247                 return entryStrategy;
248             }
249             return Optional.absent();
250         }
251     }
252
253     public static class OrderedMapModificationStrategy extends NormalizedNodeContainerModificationStrategy {
254
255         private final Optional<ModificationApplyOperation> entryStrategy;
256
257         protected OrderedMapModificationStrategy(final ListSchemaNode schema) {
258             super(OrderedMapNode.class);
259             entryStrategy = Optional.<ModificationApplyOperation> of(new ListEntryModificationStrategy(schema));
260         }
261
262         @SuppressWarnings("rawtypes")
263         @Override
264         protected NormalizedNodeContainerBuilder createBuilder(final NormalizedNode<?, ?> original) {
265             checkArgument(original instanceof OrderedMapNode);
266             return ImmutableOrderedMapNodeBuilder.create((OrderedMapNode) original);
267         }
268
269         @Override
270         public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
271             if (identifier instanceof NodeIdentifierWithPredicates) {
272                 return entryStrategy;
273             }
274             return Optional.absent();
275         }
276
277         @Override
278         public String toString() {
279             return "OrderedMapModificationStrategy [entry=" + entryStrategy + "]";
280         }
281     }
282
283     public static class UnorderedLeafSetModificationStrategy extends NormalizedNodeContainerModificationStrategy {
284
285         private final Optional<ModificationApplyOperation> entryStrategy;
286
287         @SuppressWarnings({ "unchecked", "rawtypes" })
288         protected UnorderedLeafSetModificationStrategy(final LeafListSchemaNode schema) {
289             super((Class) LeafSetNode.class);
290             entryStrategy = Optional.<ModificationApplyOperation> of(new LeafSetEntryModificationStrategy(schema));
291         }
292
293         @SuppressWarnings("rawtypes")
294         @Override
295         protected NormalizedNodeContainerBuilder createBuilder(final NormalizedNode<?, ?> original) {
296             checkArgument(original instanceof LeafSetNode<?>);
297             return ImmutableLeafSetNodeBuilder.create((LeafSetNode<?>) original);
298         }
299
300         @Override
301         public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
302             if (identifier instanceof NodeWithValue) {
303                 return entryStrategy;
304             }
305             return Optional.absent();
306         }
307     }
308
309     public static class UnorderedMapModificationStrategy extends NormalizedNodeContainerModificationStrategy {
310
311         private final Optional<ModificationApplyOperation> entryStrategy;
312
313         protected UnorderedMapModificationStrategy(final ListSchemaNode schema) {
314             super(MapNode.class);
315             entryStrategy = Optional.<ModificationApplyOperation> of(new ListEntryModificationStrategy(schema));
316         }
317
318         @SuppressWarnings("rawtypes")
319         @Override
320         protected NormalizedNodeContainerBuilder createBuilder(final NormalizedNode<?, ?> original) {
321             checkArgument(original instanceof MapNode);
322             return ImmutableMapNodeBuilder.create((MapNode) original);
323         }
324
325         @Override
326         public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
327             if (identifier instanceof NodeIdentifierWithPredicates) {
328                 return entryStrategy;
329             }
330             return Optional.absent();
331         }
332
333         @Override
334         public String toString() {
335             return "UnorderedMapModificationStrategy [entry=" + entryStrategy + "]";
336         }
337     }
338 }