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