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