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