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