Bug 499: Initial implementation of data tree modifications
[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.Set;
6
7 import org.opendaylight.controller.md.sal.dom.store.impl.tree.NodeModification;
8 import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
9 import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreNodeCompositeBuilder;
10 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
11 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
12 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeWithValue;
13 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
14 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
15 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
16 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
17 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
18 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
19 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
20 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
21 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
22 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
23 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
24 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
28
29 import com.google.common.base.Optional;
30 import com.google.common.collect.ImmutableSet;
31 import com.google.common.collect.ImmutableSet.Builder;
32 import com.google.common.primitives.UnsignedLong;
33
34 public abstract class SchemaAwareApplyOperation implements ModificationApplyOperation {
35
36     public static SchemaAwareApplyOperation from(final DataSchemaNode schemaNode) {
37         if (schemaNode instanceof ContainerSchemaNode) {
38             return new ContainerModificationStrategy((ContainerSchemaNode) schemaNode);
39         } else if (schemaNode instanceof ListSchemaNode) {
40             return new ListMapModificationStrategy((ListSchemaNode) schemaNode);
41         } else if (schemaNode instanceof ChoiceNode) {
42             return new ChoiceModificationStrategy((ChoiceNode) schemaNode);
43         } else if (schemaNode instanceof LeafListSchemaNode) {
44             return new LeafSetEntryModificationStrategy((LeafListSchemaNode) schemaNode);
45         } else if (schemaNode instanceof LeafSchemaNode) {
46             return new LeafModificationStrategy((LeafSchemaNode) schemaNode);
47         }
48         throw new IllegalArgumentException("Not supported schema node type for " + schemaNode.getClass());
49     }
50
51     @Override
52     public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
53         throw new IllegalArgumentException();
54     }
55
56     protected final ModificationApplyOperation resolveChildOperation(final PathArgument child) {
57         Optional<ModificationApplyOperation> potential = getChild(child);
58         checkArgument(potential.isPresent(), "Operation for child %s is not defined.", child);
59         return potential.get();
60     }
61
62     @Override
63     public final Optional<StoreMetadataNode> apply(final NodeModification modification,
64             final Optional<StoreMetadataNode> currentMeta) {
65         switch (modification.getModificationType()) {
66         case DELETE:
67             return Optional.absent();
68         case SUBTREE_MODIFIED:
69             return Optional.of(applySubtreeChange(modification, currentMeta.get()));
70         case WRITE:
71             return Optional.of(applyWrite(modification, currentMeta));
72         case UNMODIFIED:
73             return currentMeta;
74         default:
75             throw new IllegalArgumentException("Provided modification type is not supported.");
76         }
77     }
78
79     protected abstract StoreMetadataNode applyWrite(NodeModification modification,
80             Optional<StoreMetadataNode> currentMeta);
81
82     protected abstract StoreMetadataNode applySubtreeChange(NodeModification modification, StoreMetadataNode currentMeta);
83
84     public static abstract class ValueNodeModificationStrategy<T extends DataSchemaNode> extends
85             SchemaAwareApplyOperation {
86
87         private final T schema;
88
89         protected ValueNodeModificationStrategy(final T schema) {
90             super();
91             this.schema = schema;
92         }
93
94         @Override
95         public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
96             throw new UnsupportedOperationException("Node " + schema.getPath()
97                     + "is leaf type node. Child nodes not allowed");
98         }
99
100         @Override
101         protected StoreMetadataNode applySubtreeChange(final NodeModification modification, final StoreMetadataNode currentMeta) {
102             throw new UnsupportedOperationException("Node " + schema.getPath()
103                     + "is leaf type node. Subtree change is not allowed.");
104         }
105
106         @Override
107         protected StoreMetadataNode applyWrite(final NodeModification modification, final Optional<StoreMetadataNode> currentMeta) {
108             return StoreMetadataNode.builder()
109             // FIXME Add .increaseNodeVersion()
110                     .setData(modification.getWritenValue()).build();
111         }
112
113     }
114
115     public static class LeafSetEntryModificationStrategy extends ValueNodeModificationStrategy<LeafListSchemaNode> {
116
117         protected LeafSetEntryModificationStrategy(final LeafListSchemaNode schema) {
118             super(schema);
119         }
120     }
121
122     public static class LeafModificationStrategy extends ValueNodeModificationStrategy<LeafSchemaNode> {
123
124         protected LeafModificationStrategy(final LeafSchemaNode schema) {
125             super(schema);
126         }
127     }
128
129     public static abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareApplyOperation {
130
131         @Override
132         protected StoreMetadataNode applyWrite(final NodeModification modification, final Optional<StoreMetadataNode> currentMeta) {
133             //
134             NormalizedNode<?, ?> newValue = modification.getWritenValue();
135
136             StoreMetadataNode newValueMeta = StoreMetadataNode.createRecursivelly(newValue, UnsignedLong.valueOf(0));
137
138             if(!modification.hasAdditionalModifications()) {
139                 return newValueMeta;
140             }
141             StoreNodeCompositeBuilder builder = StoreNodeCompositeBuilder.from(newValueMeta,
142                     createBuilder(modification.getIdentifier()));
143
144             Set<PathArgument> processedPreexisting = applyPreexistingChildren(modification, newValueMeta.getChildren(), builder);
145             applyNewChildren(modification, processedPreexisting, builder);
146
147             return builder.build();
148
149         }
150
151         @Override
152         @SuppressWarnings("rawtypes")
153         public StoreMetadataNode applySubtreeChange(final NodeModification modification, final StoreMetadataNode currentMeta) {
154
155             StoreNodeCompositeBuilder builder = StoreNodeCompositeBuilder.from(currentMeta,
156                     createBuilder(modification.getIdentifier()));
157             builder.setIdentifier(modification.getIdentifier());
158
159             // We process preexisting nodes
160             Set<PathArgument> processedPreexisting = applyPreexistingChildren(modification,
161                     currentMeta.getChildren(), builder);
162             applyNewChildren(modification, processedPreexisting, builder);
163             return builder.build();
164         }
165
166         private void applyNewChildren(final NodeModification modification, final Set<PathArgument> ignore,
167                 final StoreNodeCompositeBuilder builder) {
168             for (NodeModification childModification : modification.getModifications()) {
169                 PathArgument childIdentifier = childModification.getIdentifier();
170                 // We skip allready processed modifications
171                 if (ignore.contains(childIdentifier)) {
172                     continue;
173                 }
174                 Optional<StoreMetadataNode> childResult = resolveChildOperation(childIdentifier) //
175                         .apply(childModification, Optional.<StoreMetadataNode> absent());
176                 if (childResult.isPresent()) {
177                     builder.add(childResult.get());
178                 }
179             }
180         }
181
182         private Set<PathArgument> applyPreexistingChildren(final NodeModification modification,
183                 final Iterable<StoreMetadataNode> children, final StoreNodeCompositeBuilder nodeBuilder) {
184             Builder<PathArgument> processedModifications = ImmutableSet.<PathArgument> builder();
185             for (StoreMetadataNode childMeta : children) {
186                 PathArgument childIdentifier = childMeta.getIdentifier();
187                 // We retrieve Child modification metadata
188                 Optional<NodeModification> childModification = modification.getChild(childIdentifier);
189                 // Node is modified
190                 if (childModification.isPresent()) {
191                     processedModifications.add(childIdentifier);
192                     Optional<StoreMetadataNode> change = resolveChildOperation(childIdentifier) //
193                             .apply(childModification.get(), Optional.of(childMeta));
194                 } else {
195                     // Child is unmodified - reuse existing metadata and data
196                     // snapshot
197                     nodeBuilder.add(childMeta);
198                 }
199             }
200             return processedModifications.build();
201         }
202
203         @SuppressWarnings("rawtypes")
204         protected abstract NormalizedNodeContainerBuilder createBuilder(PathArgument identifier);
205     }
206
207     public static abstract class DataNodeContainerModificationStrategy<T extends DataNodeContainer> extends
208             NormalizedNodeContainerModificationStrategy {
209
210         private final T schema;
211
212         protected DataNodeContainerModificationStrategy(final T schema) {
213             super();
214             this.schema = schema;
215         }
216
217         protected T getSchema() {
218             return schema;
219         }
220
221         @Override
222         public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
223             DataSchemaNode child = schema.getDataChildByName(identifier.getNodeType());
224             if (child == null || child.isAugmenting()) {
225                 return Optional.absent();
226             }
227             return Optional.<ModificationApplyOperation> of(from(child));
228         }
229
230         @Override
231         @SuppressWarnings("rawtypes")
232         protected abstract DataContainerNodeBuilder createBuilder(PathArgument identifier);
233
234         @Override
235         public String toString() {
236             return getClass().getSimpleName() + " [" + schema + "]";
237         }
238
239     }
240
241     public static class ContainerModificationStrategy extends
242             DataNodeContainerModificationStrategy<ContainerSchemaNode> {
243
244         public ContainerModificationStrategy(final ContainerSchemaNode schemaNode) {
245             super(schemaNode);
246         }
247
248         @Override
249         @SuppressWarnings("rawtypes")
250         protected DataContainerNodeBuilder createBuilder(final PathArgument identifier) {
251             // TODO Auto-generated method stub
252             checkArgument(identifier instanceof NodeIdentifier);
253             return ImmutableContainerNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
254         }
255
256     }
257
258     public static class ChoiceModificationStrategy extends NormalizedNodeContainerModificationStrategy {
259
260         private final ChoiceNode schema;
261
262         public ChoiceModificationStrategy(final ChoiceNode schemaNode) {
263             this.schema = schemaNode;
264         }
265
266         @Override
267         @SuppressWarnings("rawtypes")
268         protected DataContainerNodeBuilder createBuilder(final PathArgument identifier) {
269             checkArgument(identifier instanceof NodeIdentifier);
270             return ImmutableContainerNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
271         }
272
273     }
274
275     public static class ListEntryModificationStrategy extends DataNodeContainerModificationStrategy<ListSchemaNode> {
276
277         protected ListEntryModificationStrategy(final ListSchemaNode schema) {
278             super(schema);
279         }
280
281         @Override
282         @SuppressWarnings("rawtypes")
283         protected final DataContainerNodeBuilder createBuilder(final PathArgument identifier) {
284             return ImmutableMapEntryNodeBuilder.create().withNodeIdentifier((NodeIdentifierWithPredicates) identifier);
285         }
286
287     }
288
289     public static class LeafSetModificationStrategy extends NormalizedNodeContainerModificationStrategy {
290
291         private final Optional<ModificationApplyOperation> entryStrategy;
292
293         protected LeafSetModificationStrategy(final LeafListSchemaNode schema) {
294             entryStrategy = Optional.<ModificationApplyOperation> of(new LeafSetEntryModificationStrategy(schema));
295         }
296
297         @Override
298         protected NormalizedNodeContainerBuilder createBuilder(final PathArgument identifier) {
299             return ImmutableLeafSetNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
300         }
301
302         @Override
303         public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
304             if (identifier instanceof NodeWithValue) {
305                 return entryStrategy;
306             }
307             return Optional.absent();
308         }
309
310     }
311
312     public static class ListMapModificationStrategy extends NormalizedNodeContainerModificationStrategy {
313
314         private final Optional<ModificationApplyOperation> entryStrategy;
315
316         protected ListMapModificationStrategy(final ListSchemaNode schema) {
317             entryStrategy = Optional.<ModificationApplyOperation> of(new ListEntryModificationStrategy(schema));
318         }
319
320         @Override
321         protected NormalizedNodeContainerBuilder createBuilder(final PathArgument identifier) {
322             return ImmutableMapNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
323         }
324
325         @Override
326         public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
327             if (identifier instanceof NodeIdentifierWithPredicates) {
328                 return entryStrategy;
329             }
330             return Optional.absent();
331         }
332
333         @Override
334         public String toString() {
335             return "ListMapModificationStrategy [entry=" + entryStrategy + "]";
336         }
337     }
338
339     public void verifyIdentifier(final PathArgument identifier) {
340
341     }
342
343 }