909aaae1ca2483b365acef1f4072fbb41339a0c1
[controller.git] / opendaylight / md-sal / sal-dom-broker / src / main / java / org / opendaylight / controller / md / sal / dom / store / impl / SchemaAwareApplyOperation.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;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11
12 import java.util.HashSet;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Set;
16 import java.util.concurrent.ExecutionException;
17
18 import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
19 import org.opendaylight.controller.md.sal.dom.store.impl.tree.ModificationApplyOperation;
20 import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreUtils;
21 import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.ModificationType;
22 import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.NodeModification;
23 import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.StoreMetadataNode;
24 import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.StoreNodeCompositeBuilder;
25 import org.opendaylight.yangtools.yang.common.QName;
26 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
27 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
28 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
30 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeWithValue;
31 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
32 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
40 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
41 import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
42 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
43 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
44 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
45 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
46 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAugmentationNodeBuilder;
47 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableChoiceNodeBuilder;
48 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
49 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
50 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
51 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
52 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedLeafSetNodeBuilder;
53 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedMapNodeBuilder;
54 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListEntryNodeBuilder;
55 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.AugmentationSchemaProxy;
56 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
57 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
58 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
59 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
60 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
61 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
62 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
63 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
64 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
65 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
66
67 import com.google.common.base.Function;
68 import com.google.common.base.Optional;
69 import com.google.common.base.Preconditions;
70 import com.google.common.cache.CacheBuilder;
71 import com.google.common.cache.CacheLoader;
72 import com.google.common.cache.LoadingCache;
73 import com.google.common.collect.ImmutableMap;
74 import com.google.common.primitives.UnsignedLong;
75
76 public abstract class SchemaAwareApplyOperation implements ModificationApplyOperation {
77
78     public static SchemaAwareApplyOperation from(final DataSchemaNode schemaNode) {
79         if (schemaNode instanceof ContainerSchemaNode) {
80             return new ContainerModificationStrategy((ContainerSchemaNode) schemaNode);
81         } else if (schemaNode instanceof ListSchemaNode) {
82             return fromListSchemaNode((ListSchemaNode) schemaNode);
83         } else if (schemaNode instanceof ChoiceNode) {
84             return new ChoiceModificationStrategy((ChoiceNode) schemaNode);
85         } else if (schemaNode instanceof LeafListSchemaNode) {
86             return fromLeafListSchemaNode((LeafListSchemaNode) schemaNode);
87         } else if (schemaNode instanceof LeafSchemaNode) {
88             return new LeafModificationStrategy((LeafSchemaNode) schemaNode);
89         }
90         throw new IllegalArgumentException("Not supported schema node type for " + schemaNode.getClass());
91     }
92
93     private static SchemaAwareApplyOperation fromListSchemaNode(final ListSchemaNode schemaNode) {
94         List<QName> keyDefinition = schemaNode.getKeyDefinition();
95         if (keyDefinition == null || keyDefinition.isEmpty()) {
96             return new UnkeyedListModificationStrategy(schemaNode);
97         }
98         if (schemaNode.isUserOrdered()) {
99             return new OrderedMapModificationStrategy(schemaNode);
100         }
101
102         return new UnorderedMapModificationStrategy(schemaNode);
103     }
104
105     private static SchemaAwareApplyOperation fromLeafListSchemaNode(final LeafListSchemaNode schemaNode) {
106         if(schemaNode.isUserOrdered()) {
107             return new OrderedLeafSetModificationStrategy(schemaNode);
108         } else {
109             return new UnorderedLeafSetModificationStrategy(schemaNode);
110         }
111     }
112
113
114     public static SchemaAwareApplyOperation from(final DataNodeContainer resolvedTree,
115             final AugmentationTarget augSchemas, final AugmentationIdentifier identifier) {
116         AugmentationSchema augSchema = null;
117
118         allAugments:
119             for (AugmentationSchema potential : augSchemas.getAvailableAugmentations()) {
120                 for (DataSchemaNode child : potential.getChildNodes()) {
121                     if (identifier.getPossibleChildNames().contains(child.getQName())) {
122                         augSchema = potential;
123                         break allAugments;
124                     }
125                 }
126             }
127
128         if (augSchema != null) {
129             return new AugmentationModificationStrategy(augSchema, resolvedTree);
130         }
131         return null;
132     }
133
134     protected final ModificationApplyOperation resolveChildOperation(final PathArgument child) {
135         Optional<ModificationApplyOperation> potential = getChild(child);
136         checkArgument(potential.isPresent(), "Operation for child %s is not defined.", child);
137         return potential.get();
138     }
139
140     @Override
141     public void verifyStructure(final NodeModification modification) throws IllegalArgumentException {
142         if (modification.getModificationType() == ModificationType.WRITE) {
143             verifyWrittenStructure(modification.getWrittenValue());
144         }
145     }
146
147     protected abstract void verifyWrittenStructure(NormalizedNode<?, ?> writtenValue);
148
149     @Override
150     public void checkApplicable(final InstanceIdentifier path,final NodeModification modification, final Optional<StoreMetadataNode> current) throws DataPreconditionFailedException {
151         switch (modification.getModificationType()) {
152         case DELETE:
153             checkDeleteApplicable(modification, current);
154         case SUBTREE_MODIFIED:
155             checkSubtreeModificationApplicable(path,modification, current);
156             return;
157         case WRITE:
158             checkWriteApplicable(path,modification, current);
159             return;
160         case MERGE:
161             checkMergeApplicable(path,modification,current);
162             return;
163         case UNMODIFIED:
164             return;
165         default:
166             throw new UnsupportedOperationException("Suplied modification type "+modification.getModificationType()+ "is not supported.");
167         }
168
169     }
170
171     protected void checkMergeApplicable(final InstanceIdentifier path,final NodeModification modification, final Optional<StoreMetadataNode> current) throws DataPreconditionFailedException {
172         Optional<StoreMetadataNode> original = modification.getOriginal();
173         if (original.isPresent() && current.isPresent()) {
174             /*
175              * We need to do conflict detection only and only if the value of leaf changed
176              * before two transactions. If value of leaf is unchanged between two transactions
177              * it should not cause transaction to fail, since result of this merge
178              * leads to same data.
179              */
180             if(!original.get().getData().equals(current.get().getData())) {
181
182                 checkNotConflicting(path,original.get(), current.get());
183             }
184         }
185     }
186
187     protected void checkWriteApplicable(final InstanceIdentifier path,final NodeModification modification, final Optional<StoreMetadataNode> current) throws DataPreconditionFailedException {
188         Optional<StoreMetadataNode> original = modification.getOriginal();
189         if (original.isPresent() && current.isPresent()) {
190             checkNotConflicting(path,original.get(), current.get());
191         } else if(original.isPresent()) {
192             throw new DataPreconditionFailedException(path,"Node was deleted by other transaction.");
193         }
194     }
195
196     protected static final void checkNotConflicting(final InstanceIdentifier path,final StoreMetadataNode original, final StoreMetadataNode current) throws DataPreconditionFailedException {
197         checkDataPrecondition(path, original.getNodeVersion().equals(current.getNodeVersion()),"Node was replaced by other transaction.");
198         checkDataPrecondition(path,original.getSubtreeVersion().equals(current.getSubtreeVersion()), "Node children was modified by other transaction");
199     }
200
201     protected abstract void checkSubtreeModificationApplicable(InstanceIdentifier path,final NodeModification modification,
202             final Optional<StoreMetadataNode> current) throws DataPreconditionFailedException;
203
204     private void checkDeleteApplicable(final NodeModification modification, final Optional<StoreMetadataNode> current) {
205     }
206
207     @Override
208     public final Optional<StoreMetadataNode> apply(final NodeModification modification,
209             final Optional<StoreMetadataNode> currentMeta, final UnsignedLong subtreeVersion) {
210
211         switch (modification.getModificationType()) {
212         case DELETE:
213             return modification.storeSnapshot(Optional.<StoreMetadataNode> absent());
214         case SUBTREE_MODIFIED:
215             Preconditions.checkArgument(currentMeta.isPresent(), "Metadata not available for modification",
216                     modification);
217             return modification.storeSnapshot(Optional.of(applySubtreeChange(modification, currentMeta.get(),
218                     subtreeVersion)));
219         case MERGE:
220             if(currentMeta.isPresent()) {
221                 return modification.storeSnapshot(Optional.of(applyMerge(modification,currentMeta.get(),subtreeVersion)));
222             } // Fallback to write is intentional - if node is not preexisting merge is same as write
223         case WRITE:
224             return modification.storeSnapshot(Optional.of(applyWrite(modification, currentMeta, subtreeVersion)));
225         case UNMODIFIED:
226             return currentMeta;
227         default:
228             throw new IllegalArgumentException("Provided modification type is not supported.");
229         }
230     }
231
232     protected abstract StoreMetadataNode applyMerge(NodeModification modification,
233             StoreMetadataNode currentMeta, UnsignedLong subtreeVersion);
234
235     protected abstract StoreMetadataNode applyWrite(NodeModification modification,
236             Optional<StoreMetadataNode> currentMeta, UnsignedLong subtreeVersion);
237
238     protected abstract StoreMetadataNode applySubtreeChange(NodeModification modification,
239             StoreMetadataNode currentMeta, UnsignedLong subtreeVersion);
240
241     public static abstract class ValueNodeModificationStrategy<T extends DataSchemaNode> extends SchemaAwareApplyOperation {
242
243         private final T schema;
244         private final Class<? extends NormalizedNode<?, ?>> nodeClass;
245
246         protected ValueNodeModificationStrategy(final T schema, final Class<? extends NormalizedNode<?, ?>> nodeClass) {
247             super();
248             this.schema = schema;
249             this.nodeClass = nodeClass;
250         }
251
252         @Override
253         protected void verifyWrittenStructure(final NormalizedNode<?, ?> writtenValue) {
254             checkArgument(nodeClass.isInstance(writtenValue), "Node should must be of type %s", nodeClass);
255         }
256
257         @Override
258         public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
259             throw new UnsupportedOperationException("Node " + schema.getPath()
260                     + "is leaf type node. Child nodes not allowed");
261         }
262
263         @Override
264         protected StoreMetadataNode applySubtreeChange(final NodeModification modification,
265                 final StoreMetadataNode currentMeta, final UnsignedLong subtreeVersion) {
266             throw new UnsupportedOperationException("Node " + schema.getPath()
267                     + "is leaf type node. Subtree change is not allowed.");
268         }
269
270         @Override
271         protected StoreMetadataNode applyMerge(final NodeModification modification, final StoreMetadataNode currentMeta,
272                 final UnsignedLong subtreeVersion) {
273             return applyWrite(modification, Optional.of(currentMeta), subtreeVersion);
274         }
275
276         @Override
277         protected StoreMetadataNode applyWrite(final NodeModification modification,
278                 final Optional<StoreMetadataNode> currentMeta, final UnsignedLong subtreeVersion) {
279             UnsignedLong nodeVersion = subtreeVersion;
280             return StoreMetadataNode.builder().setNodeVersion(nodeVersion).setSubtreeVersion(subtreeVersion)
281                     .setData(modification.getWrittenValue()).build();
282         }
283
284         @Override
285         protected void checkSubtreeModificationApplicable(final InstanceIdentifier path,final NodeModification modification,
286                 final Optional<StoreMetadataNode> current) throws DataPreconditionFailedException {
287             throw new DataPreconditionFailedException(path, "Subtree modification is not allowed.");
288         }
289
290     }
291
292     public static class LeafSetEntryModificationStrategy extends ValueNodeModificationStrategy<LeafListSchemaNode> {
293
294         @SuppressWarnings({ "unchecked", "rawtypes" })
295         protected LeafSetEntryModificationStrategy(final LeafListSchemaNode schema) {
296             super(schema, (Class) LeafSetEntryNode.class);
297         }
298     }
299
300     public static class LeafModificationStrategy extends ValueNodeModificationStrategy<LeafSchemaNode> {
301
302         @SuppressWarnings({ "unchecked", "rawtypes" })
303         protected LeafModificationStrategy(final LeafSchemaNode schema) {
304             super(schema, (Class) LeafNode.class);
305         }
306     }
307
308     public static abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareApplyOperation {
309
310         private final Class<? extends NormalizedNode<?, ?>> nodeClass;
311
312         protected NormalizedNodeContainerModificationStrategy(final Class<? extends NormalizedNode<?, ?>> nodeClass) {
313             this.nodeClass = nodeClass;
314         }
315
316         @Override
317         public void verifyStructure(final NodeModification modification) throws IllegalArgumentException {
318             if (modification.getModificationType() == ModificationType.WRITE) {
319
320             }
321             for (NodeModification childModification : modification.getModifications()) {
322                 resolveChildOperation(childModification.getIdentifier()).verifyStructure(childModification);
323             }
324         }
325
326         @Override
327         protected void checkWriteApplicable(final InstanceIdentifier path, final NodeModification modification,
328                 final Optional<StoreMetadataNode> current) throws DataPreconditionFailedException {
329             // FIXME: Implement proper write check for replacement of node container
330             //        prerequisite is to have transaction chain available for clients
331             //        otherwise this will break chained writes to same node.
332         }
333
334         @SuppressWarnings("rawtypes")
335         @Override
336         protected void verifyWrittenStructure(final NormalizedNode<?, ?> writtenValue) {
337             checkArgument(nodeClass.isInstance(writtenValue), "Node should must be of type %s", nodeClass);
338             checkArgument(writtenValue instanceof NormalizedNodeContainer);
339
340             NormalizedNodeContainer container = (NormalizedNodeContainer) writtenValue;
341             for (Object child : container.getValue()) {
342                 checkArgument(child instanceof NormalizedNode);
343
344                 /*
345                  * FIXME: fail-fast semantics:
346                  *
347                  * We can validate the data structure here, aborting the commit
348                  * before it ever progresses to being committed.
349                  */
350             }
351         }
352
353         @Override
354         protected StoreMetadataNode applyWrite(final NodeModification modification,
355                 final Optional<StoreMetadataNode> currentMeta, final UnsignedLong subtreeVersion) {
356
357             NormalizedNode<?, ?> newValue = modification.getWrittenValue();
358
359             final UnsignedLong nodeVersion;
360             if (currentMeta.isPresent()) {
361                 nodeVersion = StoreUtils.increase(currentMeta.get().getNodeVersion());
362             } else {
363                 nodeVersion = subtreeVersion;
364             }
365
366             final StoreMetadataNode newValueMeta = StoreMetadataNode.createRecursively(newValue, nodeVersion, nodeVersion);
367             if (!modification.hasAdditionalModifications()) {
368                 return newValueMeta;
369             }
370
371             @SuppressWarnings("rawtypes")
372             NormalizedNodeContainerBuilder dataBuilder = createBuilder(newValue);
373             StoreNodeCompositeBuilder builder = StoreNodeCompositeBuilder.from(dataBuilder) //
374                     .setNodeVersion(nodeVersion) //
375                     .setSubtreeVersion(subtreeVersion);
376
377             return mutateChildren(modification.getModifications(), newValueMeta, builder, nodeVersion);
378         }
379
380         @Override
381         protected StoreMetadataNode applyMerge(final NodeModification modification, final StoreMetadataNode currentMeta,
382                 final UnsignedLong subtreeVersion) {
383             // For Node Containers - merge is same as subtree change - we only replace children.
384             return applySubtreeChange(modification, currentMeta, subtreeVersion);
385         }
386
387         @Override
388         public StoreMetadataNode applySubtreeChange(final NodeModification modification,
389                 final StoreMetadataNode currentMeta, final UnsignedLong subtreeVersion) {
390             // Bump subtree version to its new target
391             final UnsignedLong updatedSubtreeVersion = StoreUtils.increase(currentMeta.getSubtreeVersion());
392
393             @SuppressWarnings("rawtypes")
394             NormalizedNodeContainerBuilder dataBuilder = createBuilder(currentMeta.getData());
395             StoreNodeCompositeBuilder builder = StoreNodeCompositeBuilder.from(dataBuilder, currentMeta)
396                     .setIdentifier(modification.getIdentifier()).setNodeVersion(currentMeta.getNodeVersion())
397                     .setSubtreeVersion(updatedSubtreeVersion);
398
399             return mutateChildren(modification.getModifications(), currentMeta, builder, updatedSubtreeVersion);
400         }
401
402         private StoreMetadataNode mutateChildren(final Iterable<NodeModification> modifications, final StoreMetadataNode meta,
403                 final StoreNodeCompositeBuilder builder, final UnsignedLong nodeVersion) {
404
405             for (NodeModification mod : modifications) {
406                 final PathArgument id = mod.getIdentifier();
407                 final Optional<StoreMetadataNode> cm = meta.getChild(id);
408
409                 Optional<StoreMetadataNode> result = resolveChildOperation(id).apply(mod, cm, nodeVersion);
410                 if (result.isPresent()) {
411                     builder.add(result.get());
412                 } else {
413                     builder.remove(id);
414                 }
415             }
416
417             return builder.build();
418         }
419
420         @Override
421         protected void checkSubtreeModificationApplicable(final InstanceIdentifier path,final NodeModification modification,
422                 final Optional<StoreMetadataNode> current) throws DataPreconditionFailedException {
423             checkDataPrecondition(path, current.isPresent(), "Node was deleted by other transaction.");
424             checkChildPreconditions(path,modification,current);
425
426         }
427
428         private void checkChildPreconditions(final InstanceIdentifier path, final NodeModification modification, final Optional<StoreMetadataNode> current) throws DataPreconditionFailedException {
429             StoreMetadataNode currentMeta = current.get();
430             for (NodeModification childMod : modification.getModifications()) {
431                 PathArgument childId = childMod.getIdentifier();
432                 Optional<StoreMetadataNode> childMeta = currentMeta.getChild(childId);
433                 InstanceIdentifier childPath = StoreUtils.append(path, childId);
434                 resolveChildOperation(childId).checkApplicable(childPath,childMod, childMeta);
435             }
436         }
437
438         @Override
439         protected void checkMergeApplicable(final InstanceIdentifier path, final NodeModification modification,
440                 final Optional<StoreMetadataNode> current) throws DataPreconditionFailedException {
441             if(current.isPresent()) {
442                 checkChildPreconditions(path,modification,current);
443             }
444         }
445
446         @SuppressWarnings("rawtypes")
447         protected abstract NormalizedNodeContainerBuilder createBuilder(NormalizedNode<?, ?> original);
448     }
449
450     public static abstract class DataNodeContainerModificationStrategy<T extends DataNodeContainer> extends NormalizedNodeContainerModificationStrategy {
451
452         private final T schema;
453         private final LoadingCache<PathArgument, ModificationApplyOperation> childCache = CacheBuilder.newBuilder()
454                 .build(CacheLoader.from(new Function<PathArgument, ModificationApplyOperation>() {
455
456                     @Override
457                     public ModificationApplyOperation apply(final PathArgument identifier) {
458                         if (identifier instanceof AugmentationIdentifier && schema instanceof AugmentationTarget) {
459                             return from(schema, (AugmentationTarget) schema, (AugmentationIdentifier) identifier);
460                         }
461
462                         DataSchemaNode child = schema.getDataChildByName(identifier.getNodeType());
463                         if (child == null) {
464                             return null;
465                         }
466                         return from(child);
467                     }
468                 }));
469
470         protected DataNodeContainerModificationStrategy(final T schema,
471                 final Class<? extends NormalizedNode<?, ?>> nodeClass) {
472             super(nodeClass);
473             this.schema = schema;
474         }
475
476         protected T getSchema() {
477             return schema;
478         }
479
480         @Override
481         public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
482             try {
483                 return Optional.<ModificationApplyOperation> fromNullable(childCache.get(identifier));
484             } catch (ExecutionException e) {
485                 return Optional.absent();
486             }
487         }
488
489         @Override
490         @SuppressWarnings("rawtypes")
491         protected abstract DataContainerNodeBuilder createBuilder(NormalizedNode<?, ?> original);
492
493         @Override
494         public String toString() {
495             return getClass().getSimpleName() + " [" + schema + "]";
496         }
497
498     }
499
500     public static class ContainerModificationStrategy extends DataNodeContainerModificationStrategy<ContainerSchemaNode> {
501
502         public ContainerModificationStrategy(final ContainerSchemaNode schemaNode) {
503             super(schemaNode, ContainerNode.class);
504         }
505
506         @Override
507         @SuppressWarnings("rawtypes")
508         protected DataContainerNodeBuilder createBuilder(final NormalizedNode<?, ?> original) {
509             checkArgument(original instanceof ContainerNode);
510             return ImmutableContainerNodeBuilder.create((ContainerNode) original);
511         }
512     }
513
514     public static class UnkeyedListItemModificationStrategy extends DataNodeContainerModificationStrategy<ListSchemaNode> {
515
516         public UnkeyedListItemModificationStrategy(final ListSchemaNode schemaNode) {
517             super(schemaNode, UnkeyedListEntryNode.class);
518         }
519
520         @Override
521         @SuppressWarnings("rawtypes")
522         protected DataContainerNodeBuilder createBuilder(final NormalizedNode<?, ?> original) {
523             checkArgument(original instanceof UnkeyedListEntryNode);
524             return ImmutableUnkeyedListEntryNodeBuilder.create((UnkeyedListEntryNode) original);
525         }
526     }
527
528     public static class AugmentationModificationStrategy extends DataNodeContainerModificationStrategy<AugmentationSchema> {
529
530         protected AugmentationModificationStrategy(final AugmentationSchema schema, final DataNodeContainer resolved) {
531             super(createAugmentProxy(schema,resolved), AugmentationNode.class);
532         }
533
534         @Override
535         @SuppressWarnings("rawtypes")
536         protected DataContainerNodeBuilder createBuilder(final NormalizedNode<?, ?> original) {
537             checkArgument(original instanceof AugmentationNode);
538             return ImmutableAugmentationNodeBuilder.create((AugmentationNode) original);
539         }
540     }
541
542     public static class ChoiceModificationStrategy extends NormalizedNodeContainerModificationStrategy {
543
544         private final Map<PathArgument, ModificationApplyOperation> childNodes;
545
546         public ChoiceModificationStrategy(final ChoiceNode schemaNode) {
547             super(org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode.class);
548             ImmutableMap.Builder<PathArgument, ModificationApplyOperation> child = ImmutableMap.builder();
549
550             for (ChoiceCaseNode caze : schemaNode.getCases()) {
551                 for (DataSchemaNode cazeChild : caze.getChildNodes()) {
552                     SchemaAwareApplyOperation childNode = from(cazeChild);
553                     child.put(new NodeIdentifier(cazeChild.getQName()), childNode);
554                 }
555             }
556             childNodes = child.build();
557         }
558
559         @Override
560         public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
561             return Optional.fromNullable(childNodes.get(child));
562         }
563
564         @Override
565         @SuppressWarnings("rawtypes")
566         protected DataContainerNodeBuilder createBuilder(final NormalizedNode<?, ?> original) {
567             checkArgument(original instanceof org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode);
568             return ImmutableChoiceNodeBuilder.create((org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode) original);
569         }
570     }
571
572     public static class ListEntryModificationStrategy extends DataNodeContainerModificationStrategy<ListSchemaNode> {
573
574         protected ListEntryModificationStrategy(final ListSchemaNode schema) {
575             super(schema, MapEntryNode.class);
576         }
577
578         @Override
579         @SuppressWarnings("rawtypes")
580         protected final DataContainerNodeBuilder createBuilder(final NormalizedNode<?, ?> original) {
581             checkArgument(original instanceof MapEntryNode);
582             return ImmutableMapEntryNodeBuilder.create((MapEntryNode) original);
583         }
584     }
585
586     public static class UnorderedLeafSetModificationStrategy extends NormalizedNodeContainerModificationStrategy {
587
588         private final Optional<ModificationApplyOperation> entryStrategy;
589
590         @SuppressWarnings({ "unchecked", "rawtypes" })
591         protected UnorderedLeafSetModificationStrategy(final LeafListSchemaNode schema) {
592             super((Class) LeafSetNode.class);
593             entryStrategy = Optional.<ModificationApplyOperation> of(new LeafSetEntryModificationStrategy(schema));
594         }
595
596         @SuppressWarnings("rawtypes")
597         @Override
598         protected NormalizedNodeContainerBuilder createBuilder(final NormalizedNode<?, ?> original) {
599             checkArgument(original instanceof LeafSetNode<?>);
600             return ImmutableLeafSetNodeBuilder.create((LeafSetNode<?>) original);
601         }
602
603         @Override
604         public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
605             if (identifier instanceof NodeWithValue) {
606                 return entryStrategy;
607             }
608             return Optional.absent();
609         }
610     }
611
612     public static class OrderedLeafSetModificationStrategy extends NormalizedNodeContainerModificationStrategy {
613
614         private final Optional<ModificationApplyOperation> entryStrategy;
615
616         @SuppressWarnings({ "unchecked", "rawtypes" })
617         protected OrderedLeafSetModificationStrategy(final LeafListSchemaNode schema) {
618             super((Class) LeafSetNode.class);
619             entryStrategy = Optional.<ModificationApplyOperation> of(new LeafSetEntryModificationStrategy(schema));
620         }
621
622         @SuppressWarnings("rawtypes")
623         @Override
624         protected NormalizedNodeContainerBuilder createBuilder(final NormalizedNode<?, ?> original) {
625             checkArgument(original instanceof OrderedLeafSetNode<?>);
626             return ImmutableOrderedLeafSetNodeBuilder.create((OrderedLeafSetNode<?>) original);
627         }
628
629         @Override
630         public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
631             if (identifier instanceof NodeWithValue) {
632                 return entryStrategy;
633             }
634             return Optional.absent();
635         }
636     }
637
638     public static class UnkeyedListModificationStrategy extends SchemaAwareApplyOperation {
639
640         private final Optional<ModificationApplyOperation> entryStrategy;
641
642         protected UnkeyedListModificationStrategy(final ListSchemaNode schema) {
643             entryStrategy = Optional.<ModificationApplyOperation> of(new UnkeyedListItemModificationStrategy(schema));
644         }
645
646         @Override
647         protected StoreMetadataNode applyMerge(final NodeModification modification, final StoreMetadataNode currentMeta,
648                 final UnsignedLong subtreeVersion) {
649             return applyWrite(modification, Optional.of(currentMeta), subtreeVersion);
650         }
651
652         @Override
653         protected StoreMetadataNode applySubtreeChange(final NodeModification modification,
654                 final StoreMetadataNode currentMeta, final UnsignedLong subtreeVersion) {
655             throw new UnsupportedOperationException("UnkeyedList does not support subtree change.");
656         }
657
658         @Override
659         protected StoreMetadataNode applyWrite(final NodeModification modification,
660                 final Optional<StoreMetadataNode> currentMeta, final UnsignedLong subtreeVersion) {
661             return StoreMetadataNode.createRecursively(modification.getWrittenValue(), subtreeVersion);
662         }
663
664         @Override
665         public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
666             if (child instanceof NodeIdentifier) {
667                 return entryStrategy;
668             }
669             return Optional.absent();
670         }
671
672         @Override
673         protected void verifyWrittenStructure(final NormalizedNode<?, ?> writtenValue) {
674
675         }
676
677         @Override
678         protected void checkSubtreeModificationApplicable(final InstanceIdentifier path,final NodeModification modification,
679                 final Optional<StoreMetadataNode> current) throws DataPreconditionFailedException {
680             throw new DataPreconditionFailedException(path, "Subtree modification is not allowed.");
681         }
682
683     }
684
685     public static class UnorderedMapModificationStrategy extends NormalizedNodeContainerModificationStrategy {
686
687         private final Optional<ModificationApplyOperation> entryStrategy;
688
689         protected UnorderedMapModificationStrategy(final ListSchemaNode schema) {
690             super(MapNode.class);
691             entryStrategy = Optional.<ModificationApplyOperation> of(new ListEntryModificationStrategy(schema));
692         }
693
694         @SuppressWarnings("rawtypes")
695         @Override
696         protected NormalizedNodeContainerBuilder createBuilder(final NormalizedNode<?, ?> original) {
697             checkArgument(original instanceof MapNode);
698             return ImmutableMapNodeBuilder.create((MapNode) original);
699         }
700
701         @Override
702         public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
703             if (identifier instanceof NodeIdentifierWithPredicates) {
704                 return entryStrategy;
705             }
706             return Optional.absent();
707         }
708
709         @Override
710         public String toString() {
711             return "UnorderedMapModificationStrategy [entry=" + entryStrategy + "]";
712         }
713     }
714
715     public static class OrderedMapModificationStrategy extends NormalizedNodeContainerModificationStrategy {
716
717         private final Optional<ModificationApplyOperation> entryStrategy;
718
719         protected OrderedMapModificationStrategy(final ListSchemaNode schema) {
720             super(OrderedMapNode.class);
721             entryStrategy = Optional.<ModificationApplyOperation> of(new ListEntryModificationStrategy(schema));
722         }
723
724         @SuppressWarnings("rawtypes")
725         @Override
726         protected NormalizedNodeContainerBuilder createBuilder(final NormalizedNode<?, ?> original) {
727             checkArgument(original instanceof OrderedMapNode);
728             return ImmutableOrderedMapNodeBuilder.create((OrderedMapNode) original);
729         }
730
731         @Override
732         public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
733             if (identifier instanceof NodeIdentifierWithPredicates) {
734                 return entryStrategy;
735             }
736             return Optional.absent();
737         }
738
739         @Override
740         public String toString() {
741             return "OrderedMapModificationStrategy [entry=" + entryStrategy + "]";
742         }
743     }
744
745     public static AugmentationSchema createAugmentProxy(final AugmentationSchema schema, final DataNodeContainer resolved) {
746         Set<DataSchemaNode> realChildSchemas = new HashSet<>();
747         for(DataSchemaNode augChild : schema.getChildNodes()) {
748             realChildSchemas.add(resolved.getDataChildByName(augChild.getQName()));
749         }
750         return new AugmentationSchemaProxy(schema, realChildSchemas);
751     }
752
753     public static boolean checkDataPrecondition(final InstanceIdentifier path, final boolean condition, final String message) throws DataPreconditionFailedException {
754         if(!condition) {
755             throw new DataPreconditionFailedException(path, message);
756         }
757         return condition;
758     }
759
760 }