1 package org.opendaylight.controller.md.sal.dom.store.impl;
3 import static com.google.common.base.Preconditions.checkArgument;
7 import java.util.concurrent.ExecutionException;
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;
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;
56 public abstract class SchemaAwareApplyOperation implements ModificationApplyOperation {
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);
70 throw new IllegalArgumentException("Not supported schema node type for " + schemaNode.getClass());
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;
85 if(augSchema != null) {
86 return new AugmentationModificationStrategy(augSchema,resolvedTree);
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();
100 public void verifyStructure(final NodeModification modification) throws IllegalArgumentException {
101 if (modification.getModificationType() == ModificationType.WRITE) {
102 verifyWritenStructure(modification.getWritenValue());
106 protected abstract void verifyWritenStructure(NormalizedNode<?, ?> writenValue);
109 public boolean isApplicable(final NodeModification modification, final Optional<StoreMetadataNode> current) {
110 switch (modification.getModificationType()) {
112 return isDeleteApplicable(modification, current);
113 case SUBTREE_MODIFIED:
114 return isSubtreeModificationApplicable(modification, current);
116 return isWriteApplicable(modification, current);
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()) {
135 protected final boolean isNotConflicting(final StoreMetadataNode original, final StoreMetadataNode current) {
136 return original.getNodeVersion().equals(current.getNodeVersion())
137 && original.getSubtreeVersion().equals(current.getSubtreeVersion());
140 protected abstract boolean isSubtreeModificationApplicable(final NodeModification modification,
141 final Optional<StoreMetadataNode> current);
143 private boolean isDeleteApplicable(final NodeModification modification, final Optional<StoreMetadataNode> current) {
144 // FiXME: Add delete conflict detection.
149 public final Optional<StoreMetadataNode> apply(final NodeModification modification,
150 final Optional<StoreMetadataNode> currentMeta, final UnsignedLong subtreeVersion) {
152 switch (modification.getModificationType()) {
154 return modification.storeSnapshot(Optional.<StoreMetadataNode>absent());
155 case SUBTREE_MODIFIED:
156 return modification.storeSnapshot(Optional.of(applySubtreeChange(modification, currentMeta.get(), subtreeVersion)));
158 return modification.storeSnapshot(Optional.of(applyWrite(modification, currentMeta, subtreeVersion)));
162 throw new IllegalArgumentException("Provided modification type is not supported.");
166 protected abstract StoreMetadataNode applyWrite(NodeModification modification,
167 Optional<StoreMetadataNode> currentMeta, UnsignedLong subtreeVersion);
169 protected abstract StoreMetadataNode applySubtreeChange(NodeModification modification,
170 StoreMetadataNode currentMeta, UnsignedLong subtreeVersion);
172 public static abstract class ValueNodeModificationStrategy<T extends DataSchemaNode> extends
173 SchemaAwareApplyOperation {
175 private final T schema;
176 private final Class<? extends NormalizedNode<?, ?>> nodeClass;
178 protected ValueNodeModificationStrategy(final T schema, final Class<? extends NormalizedNode<?, ?>> nodeClass) {
180 this.schema = schema;
181 this.nodeClass = nodeClass;
185 protected void verifyWritenStructure(final NormalizedNode<?, ?> writenValue) {
186 checkArgument(nodeClass.isInstance(writenValue), "Node should must be of type %s", nodeClass);
190 public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
191 throw new UnsupportedOperationException("Node " + schema.getPath()
192 + "is leaf type node. Child nodes not allowed");
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.");
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());
210 return StoreMetadataNode.builder().setNodeVersion(nodeVersion).setSubtreeVersion(subtreeVersion)
211 .setData(modification.getWritenValue()).build();
215 protected boolean isSubtreeModificationApplicable(final NodeModification modification,
216 final Optional<StoreMetadataNode> current) {
222 public static class LeafSetEntryModificationStrategy extends ValueNodeModificationStrategy<LeafListSchemaNode> {
224 @SuppressWarnings({ "unchecked", "rawtypes" })
225 protected LeafSetEntryModificationStrategy(final LeafListSchemaNode schema) {
226 super(schema, (Class) LeafSetEntryNode.class);
230 public static class LeafModificationStrategy extends ValueNodeModificationStrategy<LeafSchemaNode> {
232 @SuppressWarnings({ "unchecked", "rawtypes" })
233 protected LeafModificationStrategy(final LeafSchemaNode schema) {
234 super(schema, (Class) LeafNode.class);
238 public static abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareApplyOperation {
240 private final Class<? extends NormalizedNode<?, ?>> nodeClass;
242 protected NormalizedNodeContainerModificationStrategy(final Class<? extends NormalizedNode<?, ?>> nodeClass) {
243 this.nodeClass = nodeClass;
247 public void verifyStructure(final NodeModification modification) throws IllegalArgumentException {
248 if (modification.getModificationType() == ModificationType.WRITE) {
251 for (NodeModification childModification : modification.getModifications()) {
252 resolveChildOperation(childModification.getIdentifier()).verifyStructure(childModification);
256 @SuppressWarnings("rawtypes")
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;
269 protected StoreMetadataNode applyWrite(final NodeModification modification,
270 final Optional<StoreMetadataNode> currentMeta, final UnsignedLong subtreeVersion) {
272 NormalizedNode<?, ?> newValue = modification.getWritenValue();
274 UnsignedLong nodeVersion = subtreeVersion;
275 if (currentMeta.isPresent()) {
276 nodeVersion = StoreUtils.increase(currentMeta.get().getNodeVersion());
278 StoreMetadataNode newValueMeta = StoreMetadataNode.createRecursively(newValue, nodeVersion, nodeVersion);
280 if (!modification.hasAdditionalModifications()) {
283 @SuppressWarnings("rawtypes")
284 NormalizedNodeContainerBuilder dataBuilder = createBuilder(modification.getIdentifier());
285 StoreNodeCompositeBuilder builder = StoreNodeCompositeBuilder.from(dataBuilder) //
286 .setNodeVersion(nodeVersion) //
287 .setSubtreeVersion(subtreeVersion);
289 Set<PathArgument> processedPreexisting = applyPreexistingChildren(modification, newValueMeta.getChildren(),
290 builder, nodeVersion);
291 applyNewChildren(modification, processedPreexisting, builder, nodeVersion);
293 return builder.build();
298 public StoreMetadataNode applySubtreeChange(final NodeModification modification,
299 final StoreMetadataNode currentMeta, final UnsignedLong subtreeVersion) {
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();
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)) {
323 builder.addIfPresent(resolveChildOperation(childIdentifier) //
324 .apply(childModification, Optional.<StoreMetadataNode> absent(), subtreeVersion));
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);
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);
343 // Child is unmodified - reuse existing metadata and data
345 nodeBuilder.add(childMeta);
348 return processedModifications.build();
352 protected boolean isSubtreeModificationApplicable(final NodeModification modification,
353 final Optional<StoreMetadataNode> current) {
354 if (false == current.isPresent()) {
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);
367 @SuppressWarnings("rawtypes")
368 protected abstract NormalizedNodeContainerBuilder createBuilder(PathArgument identifier);
371 public static abstract class DataNodeContainerModificationStrategy<T extends DataNodeContainer> extends
372 NormalizedNodeContainerModificationStrategy {
374 private final T schema;
375 private final LoadingCache<PathArgument, ModificationApplyOperation> childCache = CacheBuilder.newBuilder().build(
376 CacheLoader.from(new Function<PathArgument, ModificationApplyOperation>() {
379 public ModificationApplyOperation apply(final PathArgument identifier) {
380 if (identifier instanceof AugmentationIdentifier && schema instanceof AugmentationTarget) {
381 return from(schema, (AugmentationTarget) schema, (AugmentationIdentifier) identifier);
384 DataSchemaNode child = schema.getDataChildByName(identifier.getNodeType());
392 protected DataNodeContainerModificationStrategy(final T schema,
393 final Class<? extends NormalizedNode<?, ?>> nodeClass) {
395 this.schema = schema;
398 protected T getSchema() {
403 public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
405 return Optional.<ModificationApplyOperation> fromNullable(childCache.get(identifier));
406 } catch (ExecutionException e) {
407 return Optional.absent();
412 @SuppressWarnings("rawtypes")
413 protected abstract DataContainerNodeBuilder createBuilder(PathArgument identifier);
416 public String toString() {
417 return getClass().getSimpleName() + " [" + schema + "]";
422 public static class ContainerModificationStrategy extends
423 DataNodeContainerModificationStrategy<ContainerSchemaNode> {
425 public ContainerModificationStrategy(final ContainerSchemaNode schemaNode) {
426 super(schemaNode, ContainerNode.class);
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);
439 public static class AugmentationModificationStrategy extends
440 DataNodeContainerModificationStrategy<AugmentationSchema> {
442 protected AugmentationModificationStrategy(final AugmentationSchema schema, final DataNodeContainer resolved) {
443 super(schema, AugmentationNode.class);
444 // FIXME: Use resolved children instead of unresolved.
450 protected DataContainerNodeBuilder createBuilder(final PathArgument identifier) {
451 return Builders.augmentationBuilder().withNodeIdentifier((AugmentationIdentifier) identifier);
456 public static class ChoiceModificationStrategy extends NormalizedNodeContainerModificationStrategy {
458 private final ChoiceNode schema;
459 private final Map<PathArgument,ModificationApplyOperation> childNodes;
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();
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);
472 childNodes = child.build();
476 public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
477 return Optional.fromNullable(childNodes.get(child));
481 @SuppressWarnings("rawtypes")
482 protected DataContainerNodeBuilder createBuilder(final PathArgument identifier) {
483 checkArgument(identifier instanceof NodeIdentifier);
484 return ImmutableChoiceNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
489 public static class ListEntryModificationStrategy extends DataNodeContainerModificationStrategy<ListSchemaNode> {
491 protected ListEntryModificationStrategy(final ListSchemaNode schema) {
492 super(schema, MapEntryNode.class);
496 @SuppressWarnings("rawtypes")
497 protected final DataContainerNodeBuilder createBuilder(final PathArgument identifier) {
498 return ImmutableMapEntryNodeBuilder.create().withNodeIdentifier((NodeIdentifierWithPredicates) identifier);
503 public static class LeafSetModificationStrategy extends NormalizedNodeContainerModificationStrategy {
505 private final Optional<ModificationApplyOperation> entryStrategy;
507 @SuppressWarnings({ "unchecked", "rawtypes" })
508 protected LeafSetModificationStrategy(final LeafListSchemaNode schema) {
509 super((Class) LeafSetNode.class);
510 entryStrategy = Optional.<ModificationApplyOperation> of(new LeafSetEntryModificationStrategy(schema));
513 @SuppressWarnings("rawtypes")
515 protected NormalizedNodeContainerBuilder createBuilder(final PathArgument identifier) {
516 return ImmutableLeafSetNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
520 public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
521 if (identifier instanceof NodeWithValue) {
522 return entryStrategy;
524 return Optional.absent();
529 public static class ListMapModificationStrategy extends NormalizedNodeContainerModificationStrategy {
531 private final Optional<ModificationApplyOperation> entryStrategy;
533 protected ListMapModificationStrategy(final ListSchemaNode schema) {
534 super(MapNode.class);
535 entryStrategy = Optional.<ModificationApplyOperation> of(new ListEntryModificationStrategy(schema));
538 @SuppressWarnings("rawtypes")
540 protected NormalizedNodeContainerBuilder createBuilder(final PathArgument identifier) {
541 return ImmutableMapNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
545 public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
546 if (identifier instanceof NodeIdentifierWithPredicates) {
547 return entryStrategy;
549 return Optional.absent();
553 public String toString() {
554 return "ListMapModificationStrategy [entry=" + entryStrategy + "]";
558 public void verifyIdentifier(final PathArgument identifier) {