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