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.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;
57 public abstract class SchemaAwareApplyOperation implements ModificationApplyOperation {
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);
71 throw new IllegalArgumentException("Not supported schema node type for " + schemaNode.getClass());
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;
86 if(augSchema != null) {
87 return new AugmentationModificationStrategy(augSchema,resolvedTree);
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();
101 public void verifyStructure(final NodeModification modification) throws IllegalArgumentException {
102 if (modification.getModificationType() == ModificationType.WRITE) {
103 verifyWritenStructure(modification.getWritenValue());
107 protected abstract void verifyWritenStructure(NormalizedNode<?, ?> writenValue);
110 public boolean isApplicable(final NodeModification modification, final Optional<StoreMetadataNode> current) {
111 switch (modification.getModificationType()) {
113 return isDeleteApplicable(modification, current);
114 case SUBTREE_MODIFIED:
115 return isSubtreeModificationApplicable(modification, current);
117 return isWriteApplicable(modification, current);
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()) {
136 protected final boolean isNotConflicting(final StoreMetadataNode original, final StoreMetadataNode current) {
137 return original.getNodeVersion().equals(current.getNodeVersion())
138 && original.getSubtreeVersion().equals(current.getSubtreeVersion());
141 protected abstract boolean isSubtreeModificationApplicable(final NodeModification modification,
142 final Optional<StoreMetadataNode> current);
144 private boolean isDeleteApplicable(final NodeModification modification, final Optional<StoreMetadataNode> current) {
145 // FiXME: Add delete conflict detection.
150 public final Optional<StoreMetadataNode> apply(final NodeModification modification,
151 final Optional<StoreMetadataNode> currentMeta, final UnsignedLong subtreeVersion) {
153 switch (modification.getModificationType()) {
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)));
160 return modification.storeSnapshot(Optional.of(applyWrite(modification, currentMeta, subtreeVersion)));
164 throw new IllegalArgumentException("Provided modification type is not supported.");
168 protected abstract StoreMetadataNode applyWrite(NodeModification modification,
169 Optional<StoreMetadataNode> currentMeta, UnsignedLong subtreeVersion);
171 protected abstract StoreMetadataNode applySubtreeChange(NodeModification modification,
172 StoreMetadataNode currentMeta, UnsignedLong subtreeVersion);
174 public static abstract class ValueNodeModificationStrategy<T extends DataSchemaNode> extends
175 SchemaAwareApplyOperation {
177 private final T schema;
178 private final Class<? extends NormalizedNode<?, ?>> nodeClass;
180 protected ValueNodeModificationStrategy(final T schema, final Class<? extends NormalizedNode<?, ?>> nodeClass) {
182 this.schema = schema;
183 this.nodeClass = nodeClass;
187 protected void verifyWritenStructure(final NormalizedNode<?, ?> writenValue) {
188 checkArgument(nodeClass.isInstance(writenValue), "Node should must be of type %s", nodeClass);
192 public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
193 throw new UnsupportedOperationException("Node " + schema.getPath()
194 + "is leaf type node. Child nodes not allowed");
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.");
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());
212 return StoreMetadataNode.builder().setNodeVersion(nodeVersion).setSubtreeVersion(subtreeVersion)
213 .setData(modification.getWritenValue()).build();
217 protected boolean isSubtreeModificationApplicable(final NodeModification modification,
218 final Optional<StoreMetadataNode> current) {
224 public static class LeafSetEntryModificationStrategy extends ValueNodeModificationStrategy<LeafListSchemaNode> {
226 @SuppressWarnings({ "unchecked", "rawtypes" })
227 protected LeafSetEntryModificationStrategy(final LeafListSchemaNode schema) {
228 super(schema, (Class) LeafSetEntryNode.class);
232 public static class LeafModificationStrategy extends ValueNodeModificationStrategy<LeafSchemaNode> {
234 @SuppressWarnings({ "unchecked", "rawtypes" })
235 protected LeafModificationStrategy(final LeafSchemaNode schema) {
236 super(schema, (Class) LeafNode.class);
240 public static abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareApplyOperation {
242 private final Class<? extends NormalizedNode<?, ?>> nodeClass;
244 protected NormalizedNodeContainerModificationStrategy(final Class<? extends NormalizedNode<?, ?>> nodeClass) {
245 this.nodeClass = nodeClass;
249 public void verifyStructure(final NodeModification modification) throws IllegalArgumentException {
250 if (modification.getModificationType() == ModificationType.WRITE) {
253 for (NodeModification childModification : modification.getModifications()) {
254 resolveChildOperation(childModification.getIdentifier()).verifyStructure(childModification);
258 @SuppressWarnings("rawtypes")
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;
271 protected StoreMetadataNode applyWrite(final NodeModification modification,
272 final Optional<StoreMetadataNode> currentMeta, final UnsignedLong subtreeVersion) {
274 NormalizedNode<?, ?> newValue = modification.getWritenValue();
276 UnsignedLong nodeVersion = subtreeVersion;
277 if (currentMeta.isPresent()) {
278 nodeVersion = StoreUtils.increase(currentMeta.get().getNodeVersion());
280 StoreMetadataNode newValueMeta = StoreMetadataNode.createRecursively(newValue, nodeVersion, nodeVersion);
282 if (!modification.hasAdditionalModifications()) {
285 @SuppressWarnings("rawtypes")
286 NormalizedNodeContainerBuilder dataBuilder = createBuilder(modification.getIdentifier());
287 StoreNodeCompositeBuilder builder = StoreNodeCompositeBuilder.from(dataBuilder) //
288 .setNodeVersion(nodeVersion) //
289 .setSubtreeVersion(subtreeVersion);
291 Set<PathArgument> processedPreexisting = applyPreexistingChildren(modification, newValueMeta.getChildren(),
292 builder, nodeVersion);
293 applyNewChildren(modification, processedPreexisting, builder, nodeVersion);
295 return builder.build();
300 public StoreMetadataNode applySubtreeChange(final NodeModification modification,
301 final StoreMetadataNode currentMeta, final UnsignedLong subtreeVersion) {
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();
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)) {
325 builder.addIfPresent(resolveChildOperation(childIdentifier) //
326 .apply(childModification, Optional.<StoreMetadataNode> absent(), subtreeVersion));
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);
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);
345 // Child is unmodified - reuse existing metadata and data
347 nodeBuilder.add(childMeta);
350 return processedModifications.build();
354 protected boolean isSubtreeModificationApplicable(final NodeModification modification,
355 final Optional<StoreMetadataNode> current) {
356 if (false == current.isPresent()) {
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);
369 @SuppressWarnings("rawtypes")
370 protected abstract NormalizedNodeContainerBuilder createBuilder(PathArgument identifier);
373 public static abstract class DataNodeContainerModificationStrategy<T extends DataNodeContainer> extends
374 NormalizedNodeContainerModificationStrategy {
376 private final T schema;
377 private final LoadingCache<PathArgument, ModificationApplyOperation> childCache = CacheBuilder.newBuilder().build(
378 CacheLoader.from(new Function<PathArgument, ModificationApplyOperation>() {
381 public ModificationApplyOperation apply(final PathArgument identifier) {
382 if (identifier instanceof AugmentationIdentifier && schema instanceof AugmentationTarget) {
383 return from(schema, (AugmentationTarget) schema, (AugmentationIdentifier) identifier);
386 DataSchemaNode child = schema.getDataChildByName(identifier.getNodeType());
394 protected DataNodeContainerModificationStrategy(final T schema,
395 final Class<? extends NormalizedNode<?, ?>> nodeClass) {
397 this.schema = schema;
400 protected T getSchema() {
405 public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
407 return Optional.<ModificationApplyOperation> fromNullable(childCache.get(identifier));
408 } catch (ExecutionException e) {
409 return Optional.absent();
414 @SuppressWarnings("rawtypes")
415 protected abstract DataContainerNodeBuilder createBuilder(PathArgument identifier);
418 public String toString() {
419 return getClass().getSimpleName() + " [" + schema + "]";
424 public static class ContainerModificationStrategy extends
425 DataNodeContainerModificationStrategy<ContainerSchemaNode> {
427 public ContainerModificationStrategy(final ContainerSchemaNode schemaNode) {
428 super(schemaNode, ContainerNode.class);
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);
441 public static class AugmentationModificationStrategy extends
442 DataNodeContainerModificationStrategy<AugmentationSchema> {
444 protected AugmentationModificationStrategy(final AugmentationSchema schema, final DataNodeContainer resolved) {
445 super(schema, AugmentationNode.class);
446 // FIXME: Use resolved children instead of unresolved.
452 protected DataContainerNodeBuilder createBuilder(final PathArgument identifier) {
453 return Builders.augmentationBuilder().withNodeIdentifier((AugmentationIdentifier) identifier);
458 public static class ChoiceModificationStrategy extends NormalizedNodeContainerModificationStrategy {
460 private final ChoiceNode schema;
461 private final Map<PathArgument,ModificationApplyOperation> childNodes;
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();
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);
474 childNodes = child.build();
478 public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
479 return Optional.fromNullable(childNodes.get(child));
483 @SuppressWarnings("rawtypes")
484 protected DataContainerNodeBuilder createBuilder(final PathArgument identifier) {
485 checkArgument(identifier instanceof NodeIdentifier);
486 return ImmutableChoiceNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
491 public static class ListEntryModificationStrategy extends DataNodeContainerModificationStrategy<ListSchemaNode> {
493 protected ListEntryModificationStrategy(final ListSchemaNode schema) {
494 super(schema, MapEntryNode.class);
498 @SuppressWarnings("rawtypes")
499 protected final DataContainerNodeBuilder createBuilder(final PathArgument identifier) {
500 return ImmutableMapEntryNodeBuilder.create().withNodeIdentifier((NodeIdentifierWithPredicates) identifier);
505 public static class LeafSetModificationStrategy extends NormalizedNodeContainerModificationStrategy {
507 private final Optional<ModificationApplyOperation> entryStrategy;
509 @SuppressWarnings({ "unchecked", "rawtypes" })
510 protected LeafSetModificationStrategy(final LeafListSchemaNode schema) {
511 super((Class) LeafSetNode.class);
512 entryStrategy = Optional.<ModificationApplyOperation> of(new LeafSetEntryModificationStrategy(schema));
515 @SuppressWarnings("rawtypes")
517 protected NormalizedNodeContainerBuilder createBuilder(final PathArgument identifier) {
518 return ImmutableLeafSetNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
522 public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
523 if (identifier instanceof NodeWithValue) {
524 return entryStrategy;
526 return Optional.absent();
531 public static class ListMapModificationStrategy extends NormalizedNodeContainerModificationStrategy {
533 private final Optional<ModificationApplyOperation> entryStrategy;
535 protected ListMapModificationStrategy(final ListSchemaNode schema) {
536 super(MapNode.class);
537 entryStrategy = Optional.<ModificationApplyOperation> of(new ListEntryModificationStrategy(schema));
540 @SuppressWarnings("rawtypes")
542 protected NormalizedNodeContainerBuilder createBuilder(final PathArgument identifier) {
543 return ImmutableMapNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
547 public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
548 if (identifier instanceof NodeIdentifierWithPredicates) {
549 return entryStrategy;
551 return Optional.absent();
555 public String toString() {
556 return "ListMapModificationStrategy [entry=" + entryStrategy + "]";
560 public void verifyIdentifier(final PathArgument identifier) {