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