1 package org.opendaylight.controller.md.sal.dom.store.impl;
3 import static com.google.common.base.Preconditions.checkArgument;
7 import org.opendaylight.controller.md.sal.dom.store.impl.tree.ModificationType;
8 import org.opendaylight.controller.md.sal.dom.store.impl.tree.NodeModification;
9 import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
10 import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreNodeCompositeBuilder;
11 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
12 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
13 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeWithValue;
14 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
15 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
16 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
17 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
18 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
19 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
20 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
21 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
22 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
23 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
24 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
25 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
26 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
27 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
28 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
29 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
30 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
32 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
37 import com.google.common.base.Function;
38 import com.google.common.base.Optional;
39 import com.google.common.cache.Cache;
40 import com.google.common.cache.CacheBuilder;
41 import com.google.common.cache.CacheLoader;
42 import com.google.common.collect.ImmutableSet;
43 import com.google.common.collect.ImmutableSet.Builder;
44 import com.google.common.primitives.UnsignedLong;
46 public abstract class SchemaAwareApplyOperation implements ModificationApplyOperation {
48 public static SchemaAwareApplyOperation from(final DataSchemaNode schemaNode) {
49 if (schemaNode instanceof ContainerSchemaNode) {
50 return new ContainerModificationStrategy((ContainerSchemaNode) schemaNode);
51 } else if (schemaNode instanceof ListSchemaNode) {
52 return new ListMapModificationStrategy((ListSchemaNode) schemaNode);
53 } else if (schemaNode instanceof ChoiceNode) {
54 return new ChoiceModificationStrategy((ChoiceNode) schemaNode);
55 } else if (schemaNode instanceof LeafListSchemaNode) {
56 return new LeafSetEntryModificationStrategy((LeafListSchemaNode) schemaNode);
57 } else if (schemaNode instanceof LeafSchemaNode) {
58 return new LeafModificationStrategy((LeafSchemaNode) schemaNode);
60 throw new IllegalArgumentException("Not supported schema node type for " + schemaNode.getClass());
64 public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
65 throw new IllegalArgumentException();
68 protected final ModificationApplyOperation resolveChildOperation(final PathArgument child) {
69 Optional<ModificationApplyOperation> potential = getChild(child);
70 checkArgument(potential.isPresent(), "Operation for child %s is not defined.", child);
71 return potential.get();
75 public void verifyStructure(final NodeModification modification) throws IllegalArgumentException {
76 if (modification.getModificationType() == ModificationType.WRITE) {
77 verifyWritenStructure(modification.getWritenValue());
81 protected abstract void verifyWritenStructure(NormalizedNode<?, ?> writenValue);
84 public boolean isApplicable(final NodeModification modification, final Optional<StoreMetadataNode> current) {
85 switch (modification.getModificationType()) {
87 return isDeleteApplicable(modification, current);
88 case SUBTREE_MODIFIED:
89 return isSubtreeModificationApplicable(modification, current);
91 return isWriteApplicable(modification, current);
99 protected boolean isWriteApplicable(final NodeModification modification, final Optional<StoreMetadataNode> current) {
100 Optional<StoreMetadataNode> original = modification.getOriginal();
101 if (original.isPresent() && current.isPresent()) {
102 return isNotConflicting(original.get(), current.get());
103 } else if (current.isPresent()) {
110 protected final boolean isNotConflicting(final StoreMetadataNode original, final StoreMetadataNode current) {
111 return original.getNodeVersion().equals(current.getNodeVersion())
112 && original.getSubtreeVersion().equals(current.getSubtreeVersion());
115 protected abstract boolean isSubtreeModificationApplicable(final NodeModification modification,
116 final Optional<StoreMetadataNode> current);
118 private boolean isDeleteApplicable(final NodeModification modification, final Optional<StoreMetadataNode> current) {
119 // FiXME: Add delete conflict detection.
124 public final Optional<StoreMetadataNode> apply(final NodeModification modification,
125 final Optional<StoreMetadataNode> currentMeta, final UnsignedLong subtreeVersion) {
126 switch (modification.getModificationType()) {
128 return Optional.absent();
129 case SUBTREE_MODIFIED:
130 return Optional.of(applySubtreeChange(modification, currentMeta.get(), subtreeVersion));
132 return Optional.of(applyWrite(modification, currentMeta, subtreeVersion));
136 throw new IllegalArgumentException("Provided modification type is not supported.");
140 protected abstract StoreMetadataNode applyWrite(NodeModification modification,
141 Optional<StoreMetadataNode> currentMeta, UnsignedLong subtreeVersion);
143 protected abstract StoreMetadataNode applySubtreeChange(NodeModification modification,
144 StoreMetadataNode currentMeta, UnsignedLong subtreeVersion);
146 public static abstract class ValueNodeModificationStrategy<T extends DataSchemaNode> extends
147 SchemaAwareApplyOperation {
149 private final T schema;
150 private final Class<? extends NormalizedNode<?, ?>> nodeClass;
152 protected ValueNodeModificationStrategy(final T schema, final Class<? extends NormalizedNode<?, ?>> nodeClass) {
154 this.schema = schema;
155 this.nodeClass = nodeClass;
159 protected void verifyWritenStructure(final NormalizedNode<?, ?> writenValue) {
160 checkArgument(nodeClass.isInstance(writenValue), "Node should must be of type %s", nodeClass);
164 public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
165 throw new UnsupportedOperationException("Node " + schema.getPath()
166 + "is leaf type node. Child nodes not allowed");
170 protected StoreMetadataNode applySubtreeChange(final NodeModification modification,
171 final StoreMetadataNode currentMeta, final UnsignedLong subtreeVersion) {
172 throw new UnsupportedOperationException("Node " + schema.getPath()
173 + "is leaf type node. Subtree change is not allowed.");
177 protected StoreMetadataNode applyWrite(final NodeModification modification,
178 final Optional<StoreMetadataNode> currentMeta, final UnsignedLong subtreeVersion) {
179 UnsignedLong nodeVersion = subtreeVersion;
180 if (currentMeta.isPresent()) {
181 nodeVersion = StoreUtils.increase(currentMeta.get().getNodeVersion());
184 return StoreMetadataNode.builder().setNodeVersion(nodeVersion).setSubtreeVersion(subtreeVersion)
185 .setData(modification.getWritenValue()).build();
189 protected boolean isSubtreeModificationApplicable(final NodeModification modification,
190 final Optional<StoreMetadataNode> current) {
196 public static class LeafSetEntryModificationStrategy extends ValueNodeModificationStrategy<LeafListSchemaNode> {
198 @SuppressWarnings({ "unchecked", "rawtypes" })
199 protected LeafSetEntryModificationStrategy(final LeafListSchemaNode schema) {
200 super(schema, (Class) LeafSetEntryNode.class);
204 public static class LeafModificationStrategy extends ValueNodeModificationStrategy<LeafSchemaNode> {
206 @SuppressWarnings({ "unchecked", "rawtypes" })
207 protected LeafModificationStrategy(final LeafSchemaNode schema) {
208 super(schema, (Class) LeafNode.class);
212 public static abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareApplyOperation {
214 private final Class<? extends NormalizedNode<?, ?>> nodeClass;
216 protected NormalizedNodeContainerModificationStrategy(final Class<? extends NormalizedNode<?, ?>> nodeClass) {
217 this.nodeClass = nodeClass;
222 public void verifyStructure(final NodeModification modification) throws IllegalArgumentException {
223 if(modification.getModificationType() == ModificationType.WRITE) {
226 for(NodeModification childModification : modification.getModifications()) {
227 resolveChildOperation(childModification.getIdentifier()).verifyStructure(childModification);
231 @SuppressWarnings("rawtypes")
233 protected void verifyWritenStructure(final NormalizedNode<?, ?> writenValue) {
234 checkArgument(nodeClass.isInstance(writenValue), "Node should must be of type %s", nodeClass);
235 checkArgument(writenValue instanceof NormalizedNodeContainer);
236 NormalizedNodeContainer writenCont = (NormalizedNodeContainer) writenValue;
237 for(Object child : writenCont.getValue()) {
238 checkArgument(child instanceof NormalizedNode);
239 NormalizedNode childNode = (NormalizedNode) child;
244 protected StoreMetadataNode applyWrite(final NodeModification modification,
245 final Optional<StoreMetadataNode> currentMeta, final UnsignedLong subtreeVersion) {
247 NormalizedNode<?, ?> newValue = modification.getWritenValue();
249 UnsignedLong nodeVersion = subtreeVersion;
250 if (currentMeta.isPresent()) {
251 nodeVersion = StoreUtils.increase(currentMeta.get().getNodeVersion());
253 StoreMetadataNode newValueMeta = StoreMetadataNode.createRecursivelly(newValue, nodeVersion, nodeVersion);
255 if (!modification.hasAdditionalModifications()) {
258 @SuppressWarnings("rawtypes")
259 NormalizedNodeContainerBuilder dataBuilder = createBuilder(modification.getIdentifier());
260 StoreNodeCompositeBuilder builder = StoreNodeCompositeBuilder.from(dataBuilder) //
261 .setNodeVersion(nodeVersion) //
262 .setSubtreeVersion(subtreeVersion);
264 Set<PathArgument> processedPreexisting = applyPreexistingChildren(modification, newValueMeta.getChildren(),
265 builder, nodeVersion);
266 applyNewChildren(modification, processedPreexisting, builder, nodeVersion);
268 return builder.build();
273 public StoreMetadataNode applySubtreeChange(final NodeModification modification,
274 final StoreMetadataNode currentMeta, final UnsignedLong subtreeVersion) {
276 UnsignedLong updatedSubtreeVersion = StoreUtils.increase(currentMeta.getSubtreeVersion());
277 @SuppressWarnings("rawtypes")
278 NormalizedNodeContainerBuilder dataBuilder = createBuilder(modification.getIdentifier());
279 StoreNodeCompositeBuilder builder = StoreNodeCompositeBuilder.from(dataBuilder)
281 .setIdentifier(modification.getIdentifier()).setNodeVersion(currentMeta.getNodeVersion())
282 .setSubtreeVersion(updatedSubtreeVersion);
283 // We process preexisting nodes
284 Set<PathArgument> processedPreexisting = applyPreexistingChildren(modification, currentMeta.getChildren(),
285 builder, updatedSubtreeVersion);
286 applyNewChildren(modification, processedPreexisting, builder, updatedSubtreeVersion);
287 return builder.build();
290 private void applyNewChildren(final NodeModification modification, final Set<PathArgument> ignore,
291 final StoreNodeCompositeBuilder builder, final UnsignedLong subtreeVersion) {
292 for (NodeModification childModification : modification.getModifications()) {
293 PathArgument childIdentifier = childModification.getIdentifier();
294 // We skip allready processed modifications
295 if (ignore.contains(childIdentifier)) {
299 builder.addIfPresent(resolveChildOperation(childIdentifier) //
300 .apply(childModification, Optional.<StoreMetadataNode> absent(), subtreeVersion));
304 private Set<PathArgument> applyPreexistingChildren(final NodeModification modification,
305 final Iterable<StoreMetadataNode> children, final StoreNodeCompositeBuilder nodeBuilder,
306 final UnsignedLong subtreeVersion) {
307 Builder<PathArgument> processedModifications = ImmutableSet.<PathArgument> builder();
308 for (StoreMetadataNode childMeta : children) {
309 PathArgument childIdentifier = childMeta.getIdentifier();
310 // We retrieve Child modification metadata
311 Optional<NodeModification> childModification = modification.getChild(childIdentifier);
313 if (childModification.isPresent()) {
314 processedModifications.add(childIdentifier);
315 Optional<StoreMetadataNode> result = resolveChildOperation(childIdentifier) //
316 .apply(childModification.get(), Optional.of(childMeta), subtreeVersion);
317 nodeBuilder.addIfPresent(result);
319 // Child is unmodified - reuse existing metadata and data
321 nodeBuilder.add(childMeta);
324 return processedModifications.build();
328 protected boolean isSubtreeModificationApplicable(final NodeModification modification,
329 final Optional<StoreMetadataNode> current) {
330 if (false == current.isPresent()) {
333 boolean result = true;
334 StoreMetadataNode currentMeta = current.get();
335 for (NodeModification childMod : modification.getModifications()) {
336 PathArgument childId = childMod.getIdentifier();
337 Optional<StoreMetadataNode> childMeta = currentMeta.getChild(childId);
338 result &= resolveChildOperation(childId).isApplicable(childMod, childMeta);
343 @SuppressWarnings("rawtypes")
344 protected abstract NormalizedNodeContainerBuilder createBuilder(PathArgument identifier);
347 public static abstract class DataNodeContainerModificationStrategy<T extends DataNodeContainer> extends
348 NormalizedNodeContainerModificationStrategy {
350 private final T schema;
351 private final Cache<PathArgument, ModificationApplyOperation> childCache = CacheBuilder.newBuilder()
352 .build(CacheLoader.from(new Function<PathArgument, ModificationApplyOperation>() {
355 public ModificationApplyOperation apply(final PathArgument identifier) {
356 DataSchemaNode child = schema.getDataChildByName(identifier.getNodeType());
357 if (child == null || child.isAugmenting()) {
364 protected DataNodeContainerModificationStrategy(final T schema,
365 final Class<? extends NormalizedNode<?, ?>> nodeClass) {
367 this.schema = schema;
370 protected T getSchema() {
375 public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
376 DataSchemaNode child = schema.getDataChildByName(identifier.getNodeType());
377 if (child == null || child.isAugmenting()) {
378 return Optional.absent();
380 return Optional.<ModificationApplyOperation> of(from(child));
384 @SuppressWarnings("rawtypes")
385 protected abstract DataContainerNodeBuilder createBuilder(PathArgument identifier);
388 public String toString() {
389 return getClass().getSimpleName() + " [" + schema + "]";
394 public static class ContainerModificationStrategy extends
395 DataNodeContainerModificationStrategy<ContainerSchemaNode> {
397 public ContainerModificationStrategy(final ContainerSchemaNode schemaNode) {
398 super(schemaNode, ContainerNode.class);
402 @SuppressWarnings("rawtypes")
403 protected DataContainerNodeBuilder createBuilder(final PathArgument identifier) {
404 // TODO Auto-generated method stub
405 checkArgument(identifier instanceof NodeIdentifier);
406 return ImmutableContainerNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
411 public static class ChoiceModificationStrategy extends NormalizedNodeContainerModificationStrategy {
413 private final ChoiceNode schema;
415 public ChoiceModificationStrategy(final ChoiceNode schemaNode) {
416 super(org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode.class);
417 this.schema = schemaNode;
421 @SuppressWarnings("rawtypes")
422 protected DataContainerNodeBuilder createBuilder(final PathArgument identifier) {
423 checkArgument(identifier instanceof NodeIdentifier);
424 return ImmutableContainerNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
429 public static class ListEntryModificationStrategy extends DataNodeContainerModificationStrategy<ListSchemaNode> {
431 protected ListEntryModificationStrategy(final ListSchemaNode schema) {
432 super(schema, MapEntryNode.class);
436 @SuppressWarnings("rawtypes")
437 protected final DataContainerNodeBuilder createBuilder(final PathArgument identifier) {
438 return ImmutableMapEntryNodeBuilder.create().withNodeIdentifier((NodeIdentifierWithPredicates) identifier);
443 public static class LeafSetModificationStrategy extends NormalizedNodeContainerModificationStrategy {
445 private final Optional<ModificationApplyOperation> entryStrategy;
447 @SuppressWarnings({ "unchecked", "rawtypes" })
448 protected LeafSetModificationStrategy(final LeafListSchemaNode schema) {
449 super((Class) LeafSetNode.class);
450 entryStrategy = Optional.<ModificationApplyOperation> of(new LeafSetEntryModificationStrategy(schema));
453 @SuppressWarnings("rawtypes")
455 protected NormalizedNodeContainerBuilder createBuilder(final PathArgument identifier) {
456 return ImmutableLeafSetNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
460 public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
461 if (identifier instanceof NodeWithValue) {
462 return entryStrategy;
464 return Optional.absent();
469 public static class ListMapModificationStrategy extends NormalizedNodeContainerModificationStrategy {
471 private final Optional<ModificationApplyOperation> entryStrategy;
473 protected ListMapModificationStrategy(final ListSchemaNode schema) {
474 super(MapNode.class);
475 entryStrategy = Optional.<ModificationApplyOperation> of(new ListEntryModificationStrategy(schema));
478 @SuppressWarnings("rawtypes")
480 protected NormalizedNodeContainerBuilder createBuilder(final PathArgument identifier) {
481 return ImmutableMapNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
485 public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
486 if (identifier instanceof NodeIdentifierWithPredicates) {
487 return entryStrategy;
489 return Optional.absent();
493 public String toString() {
494 return "ListMapModificationStrategy [entry=" + entryStrategy + "]";
498 public void verifyIdentifier(final PathArgument identifier) {