Merge "Resolve Bug:717 - Remove fragment version requirement from config-persister-*"
[controller.git] / opendaylight / md-sal / sal-common-impl / src / main / java / org / opendaylight / controller / md / sal / common / impl / util / compat / DataNormalizationOperation.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.md.sal.common.impl.util.compat;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Preconditions.checkNotNull;
12
13 import java.util.Collections;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Map.Entry;
18 import java.util.Set;
19 import java.util.concurrent.ConcurrentHashMap;
20
21 import org.opendaylight.yangtools.concepts.Identifiable;
22 import org.opendaylight.yangtools.yang.common.QName;
23 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
24 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
25 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
26 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
27 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeWithValue;
28 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
29 import org.opendaylight.yangtools.yang.data.api.Node;
30 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
34 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
35 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
36 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
37 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
38 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
39 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
40 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
41 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
43 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
45 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
46 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
47 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
48
49 import com.google.common.collect.FluentIterable;
50 import com.google.common.collect.ImmutableMap;
51 import com.google.common.collect.ImmutableSet;
52
53 public abstract class DataNormalizationOperation<T extends PathArgument> implements Identifiable<T> {
54
55     private final T identifier;
56
57     @Override
58     public T getIdentifier() {
59         return identifier;
60     };
61
62     protected DataNormalizationOperation(final T identifier) {
63         super();
64         this.identifier = identifier;
65     }
66
67     public boolean isMixin() {
68         return false;
69     }
70
71
72     public boolean isKeyedEntry() {
73         return false;
74     }
75
76     protected Set<QName> getQNameIdentifiers() {
77         return Collections.singleton(identifier.getNodeType());
78     }
79
80     public abstract DataNormalizationOperation<?> getChild(final PathArgument child) throws DataNormalizationException;
81
82     public abstract DataNormalizationOperation<?> getChild(QName child) throws DataNormalizationException;
83
84     public abstract NormalizedNode<?, ?> normalize(Node<?> legacyData);
85
86     private static abstract class SimpleTypeNormalization<T extends PathArgument> extends DataNormalizationOperation<T> {
87
88         protected SimpleTypeNormalization(final T identifier) {
89             super(identifier);
90         }
91
92         @Override
93         public NormalizedNode<?, ?> normalize(final Node<?> legacyData) {
94             checkArgument(legacyData != null);
95             checkArgument(legacyData instanceof SimpleNode<?>);
96             return normalizeImpl((SimpleNode<?>) legacyData);
97         }
98
99         protected abstract NormalizedNode<?, ?> normalizeImpl(SimpleNode<?> node);
100
101         @Override
102         public DataNormalizationOperation<?> getChild(final PathArgument child) {
103             return null;
104         }
105
106         @Override
107         public DataNormalizationOperation<?> getChild(final QName child) {
108             return null;
109         }
110
111         @Override
112         public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
113             // TODO Auto-generated method stub
114             return null;
115         }
116
117     }
118
119     private static final class LeafNormalization extends SimpleTypeNormalization<NodeIdentifier> {
120
121         protected LeafNormalization(final NodeIdentifier identifier) {
122             super(identifier);
123         }
124
125         @Override
126         protected NormalizedNode<?, ?> normalizeImpl(final SimpleNode<?> node) {
127             return ImmutableNodes.leafNode(node.getNodeType(), node.getValue());
128         }
129
130     }
131
132     private static final class LeafListEntryNormalization extends SimpleTypeNormalization<NodeWithValue> {
133
134         public LeafListEntryNormalization(final LeafListSchemaNode potential) {
135             super(new NodeWithValue(potential.getQName(), null));
136         }
137
138         @Override
139         protected NormalizedNode<?, ?> normalizeImpl(final SimpleNode<?> node) {
140             NodeWithValue nodeId = new NodeWithValue(node.getNodeType(), node.getValue());
141             return Builders.leafSetEntryBuilder().withNodeIdentifier(nodeId).withValue(node.getValue()).build();
142         }
143
144
145         @Override
146         public boolean isKeyedEntry() {
147             return true;
148         }
149     }
150
151     private static abstract class CompositeNodeNormalizationOpertation<T extends PathArgument> extends
152             DataNormalizationOperation<T> {
153
154         protected CompositeNodeNormalizationOpertation(final T identifier) {
155             super(identifier);
156         }
157
158         @SuppressWarnings({ "rawtypes", "unchecked" })
159         @Override
160         public final NormalizedNodeContainer<?, ?, ?> normalize(final Node<?> legacyData) {
161             checkArgument(legacyData != null);
162             if (!isMixin() && getIdentifier().getNodeType() != null) {
163                 checkArgument(getIdentifier().getNodeType().equals(legacyData.getNodeType()),
164                         "Node QName must be %s was %s", getIdentifier().getNodeType(), legacyData.getNodeType());
165             }
166             checkArgument(legacyData instanceof CompositeNode, "Node %s should be composite", legacyData);
167             CompositeNode compositeNode = (CompositeNode) legacyData;
168             NormalizedNodeContainerBuilder builder = createBuilder(compositeNode);
169
170             Set<DataNormalizationOperation<?>> usedMixins = new HashSet<>();
171             for (Node<?> childLegacy : compositeNode.getValue()) {
172                 final DataNormalizationOperation childOp;
173
174                 try {
175                     childOp = getChild(childLegacy.getNodeType());
176                 } catch (DataNormalizationException e) {
177                     throw new IllegalArgumentException(String.format("Failed to normalize data %s", compositeNode.getValue()), e);
178                 }
179
180                 // We skip unknown nodes if this node is mixin since
181                 // it's nodes and parent nodes are interleaved
182                 if (childOp == null && isMixin()) {
183                     continue;
184                 }
185
186                 checkArgument(childOp != null, "Node %s is not allowed inside %s", childLegacy.getNodeType(),
187                         getIdentifier());
188                 if (childOp.isMixin()) {
189                     if (usedMixins.contains(childOp)) {
190                         // We already run / processed that mixin, so to avoid
191                         // duplicity we are skipping next nodes.
192                         continue;
193                     }
194                     builder.addChild(childOp.normalize(compositeNode));
195                     usedMixins.add(childOp);
196                 } else {
197                     builder.addChild(childOp.normalize(childLegacy));
198                 }
199             }
200             return (NormalizedNodeContainer<?, ?, ?>) builder.build();
201         }
202
203         @SuppressWarnings("rawtypes")
204         protected abstract NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode);
205
206     }
207
208     private static abstract class DataContainerNormalizationOperation<T extends PathArgument> extends
209             CompositeNodeNormalizationOpertation<T> {
210
211         private final DataNodeContainer schema;
212         private final Map<QName, DataNormalizationOperation<?>> byQName;
213         private final Map<PathArgument, DataNormalizationOperation<?>> byArg;
214
215         protected DataContainerNormalizationOperation(final T identifier, final DataNodeContainer schema) {
216             super(identifier);
217             this.schema = schema;
218             this.byArg = new ConcurrentHashMap<>();
219             this.byQName = new ConcurrentHashMap<>();
220         }
221
222         @Override
223         public DataNormalizationOperation<?> getChild(final PathArgument child) throws DataNormalizationException {
224             DataNormalizationOperation<?> potential = byArg.get(child);
225             if (potential != null) {
226                 return potential;
227             }
228             potential = fromSchema(schema, child);
229             return register(potential);
230         }
231
232         @Override
233         public DataNormalizationOperation<?> getChild(final QName child) throws DataNormalizationException {
234             DataNormalizationOperation<?> potential = byQName.get(child);
235             if (potential != null) {
236                 return potential;
237             }
238             potential = fromSchemaAndPathArgument(schema, child);
239             return register(potential);
240         }
241
242         private DataNormalizationOperation<?> register(final DataNormalizationOperation<?> potential) {
243             if (potential != null) {
244                 byArg.put(potential.getIdentifier(), potential);
245                 for (QName qName : potential.getQNameIdentifiers()) {
246                     byQName.put(qName, potential);
247                 }
248             }
249             return potential;
250         }
251
252     }
253
254     private static final class ListItemNormalization extends
255             DataContainerNormalizationOperation<NodeIdentifierWithPredicates> {
256
257         private final List<QName> keyDefinition;
258
259         protected ListItemNormalization(final NodeIdentifierWithPredicates identifier, final ListSchemaNode schema) {
260             super(identifier, schema);
261             keyDefinition = schema.getKeyDefinition();
262         }
263
264         @Override
265         protected NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode) {
266             ImmutableMap.Builder<QName, Object> keys = ImmutableMap.builder();
267             for (QName key : keyDefinition) {
268
269                 SimpleNode<?> valueNode = checkNotNull(compositeNode.getFirstSimpleByName(key),
270                         "List node %s MUST contain leaf %s with value.", getIdentifier().getNodeType(), key);
271                 keys.put(key, valueNode.getValue());
272             }
273
274             return Builders.mapEntryBuilder().withNodeIdentifier(
275                     new NodeIdentifierWithPredicates(getIdentifier().getNodeType(), keys.build()));
276         }
277
278         @Override
279         public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
280             DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = Builders
281                     .mapEntryBuilder().withNodeIdentifier((NodeIdentifierWithPredicates) currentArg);
282             for (Entry<QName, Object> keyValue : ((NodeIdentifierWithPredicates) currentArg).getKeyValues().entrySet()) {
283                 builder.addChild(Builders.leafBuilder()
284                         //
285                         .withNodeIdentifier(new NodeIdentifier(keyValue.getKey())).withValue(keyValue.getValue())
286                         .build());
287             }
288             return builder.build();
289         }
290
291
292         @Override
293         public boolean isKeyedEntry() {
294             return true;
295         }
296     }
297
298     private static final class ContainerNormalization extends DataContainerNormalizationOperation<NodeIdentifier> {
299
300         protected ContainerNormalization(final ContainerSchemaNode schema) {
301             super(new NodeIdentifier(schema.getQName()), schema);
302         }
303
304         @Override
305         protected NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode) {
306             return Builders.containerBuilder().withNodeIdentifier(getIdentifier());
307         }
308
309         @Override
310         public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
311             return Builders.containerBuilder().withNodeIdentifier((NodeIdentifier) currentArg).build();
312         }
313
314     }
315
316     private static abstract class MixinNormalizationOp<T extends PathArgument> extends
317             CompositeNodeNormalizationOpertation<T> {
318
319         protected MixinNormalizationOp(final T identifier) {
320             super(identifier);
321         }
322
323         @Override
324         public final boolean isMixin() {
325             return true;
326         }
327
328     }
329
330     private static final class LeafListMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
331
332         private final DataNormalizationOperation<?> innerOp;
333
334         public LeafListMixinNormalization(final LeafListSchemaNode potential) {
335             super(new NodeIdentifier(potential.getQName()));
336             innerOp = new LeafListEntryNormalization(potential);
337         }
338
339         @Override
340         protected NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode) {
341             return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier());
342         }
343
344         @Override
345         public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
346             return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier()).build();
347         }
348
349         @Override
350         public DataNormalizationOperation<?> getChild(final PathArgument child) {
351             if (child instanceof NodeWithValue) {
352                 return innerOp;
353             }
354             return null;
355         }
356
357         @Override
358         public DataNormalizationOperation<?> getChild(final QName child) {
359             if (getIdentifier().getNodeType().equals(child)) {
360                 return innerOp;
361             }
362             return null;
363         }
364     }
365
366     private static final class AugmentationNormalization extends MixinNormalizationOp<AugmentationIdentifier> {
367
368         private final Map<QName, DataNormalizationOperation<?>> byQName;
369         private final Map<PathArgument, DataNormalizationOperation<?>> byArg;
370
371         public AugmentationNormalization(final AugmentationSchema augmentation, final DataNodeContainer schema) {
372             super(augmentationIdentifierFrom(augmentation));
373
374             ImmutableMap.Builder<QName, DataNormalizationOperation<?>> byQNameBuilder = ImmutableMap.builder();
375             ImmutableMap.Builder<PathArgument, DataNormalizationOperation<?>> byArgBuilder = ImmutableMap.builder();
376
377             for (DataSchemaNode augNode : augmentation.getChildNodes()) {
378                 DataSchemaNode resolvedNode = schema.getDataChildByName(augNode.getQName());
379                 DataNormalizationOperation<?> resolvedOp = fromDataSchemaNode(resolvedNode);
380                 byArgBuilder.put(resolvedOp.getIdentifier(), resolvedOp);
381                 for (QName resQName : resolvedOp.getQNameIdentifiers()) {
382                     byQNameBuilder.put(resQName, resolvedOp);
383                 }
384             }
385             byQName = byQNameBuilder.build();
386             byArg = byArgBuilder.build();
387
388         }
389
390         @Override
391         public DataNormalizationOperation<?> getChild(final PathArgument child) {
392             return byArg.get(child);
393         }
394
395         @Override
396         public DataNormalizationOperation<?> getChild(final QName child) {
397             return byQName.get(child);
398         }
399
400         @Override
401         protected Set<QName> getQNameIdentifiers() {
402             return getIdentifier().getPossibleChildNames();
403         }
404
405         @SuppressWarnings("rawtypes")
406         @Override
407         protected NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode) {
408             return Builders.augmentationBuilder().withNodeIdentifier(getIdentifier());
409         }
410
411         @Override
412         public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
413             return Builders.augmentationBuilder().withNodeIdentifier(getIdentifier()).build();
414         }
415
416     }
417
418     private static final class ListMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
419
420         private final ListItemNormalization innerNode;
421
422         public ListMixinNormalization(final ListSchemaNode list) {
423             super(new NodeIdentifier(list.getQName()));
424             this.innerNode = new ListItemNormalization(new NodeIdentifierWithPredicates(list.getQName(),
425                     Collections.<QName, Object> emptyMap()), list);
426         }
427
428         @SuppressWarnings("rawtypes")
429         @Override
430         protected NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode) {
431             return Builders.mapBuilder().withNodeIdentifier(getIdentifier());
432         }
433
434         @Override
435         public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
436             return Builders.mapBuilder().withNodeIdentifier(getIdentifier()).build();
437         }
438
439         @Override
440         public DataNormalizationOperation<?> getChild(final PathArgument child) {
441             if (child.getNodeType().equals(getIdentifier().getNodeType())) {
442                 return innerNode;
443             }
444             return null;
445         }
446
447         @Override
448         public DataNormalizationOperation<?> getChild(final QName child) {
449             if (getIdentifier().getNodeType().equals(child)) {
450                 return innerNode;
451             }
452             return null;
453         }
454
455     }
456
457     private static class ChoiceNodeNormalization extends MixinNormalizationOp<NodeIdentifier> {
458
459         private final ImmutableMap<QName, DataNormalizationOperation<?>> byQName;
460         private final ImmutableMap<PathArgument, DataNormalizationOperation<?>> byArg;
461
462         protected ChoiceNodeNormalization(final org.opendaylight.yangtools.yang.model.api.ChoiceNode schema) {
463             super(new NodeIdentifier(schema.getQName()));
464             ImmutableMap.Builder<QName, DataNormalizationOperation<?>> byQNameBuilder = ImmutableMap.builder();
465             ImmutableMap.Builder<PathArgument, DataNormalizationOperation<?>> byArgBuilder = ImmutableMap.builder();
466
467             for (ChoiceCaseNode caze : schema.getCases()) {
468                 for (DataSchemaNode cazeChild : caze.getChildNodes()) {
469                     DataNormalizationOperation<?> childOp = fromDataSchemaNode(cazeChild);
470                     byArgBuilder.put(childOp.getIdentifier(), childOp);
471                     for (QName qname : childOp.getQNameIdentifiers()) {
472                         byQNameBuilder.put(qname, childOp);
473                     }
474                 }
475             }
476             byQName = byQNameBuilder.build();
477             byArg = byArgBuilder.build();
478         }
479
480         @Override
481         public DataNormalizationOperation<?> getChild(final PathArgument child) {
482             return byArg.get(child);
483         }
484
485         @Override
486         public DataNormalizationOperation<?> getChild(final QName child) {
487             return byQName.get(child);
488         }
489
490         @Override
491         protected NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode) {
492             return Builders.choiceBuilder().withNodeIdentifier(getIdentifier());
493         }
494
495         @Override
496         public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
497             return Builders.choiceBuilder().withNodeIdentifier(getIdentifier()).build();
498         }
499     }
500
501     private static DataNormalizationOperation<?> fromSchemaAndPathArgument(final DataNodeContainer schema,
502             final QName child) throws DataNormalizationException {
503         DataSchemaNode potential = schema.getDataChildByName(child);
504         if (potential == null) {
505             Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode> choices = FluentIterable.from(
506                     schema.getChildNodes()).filter(org.opendaylight.yangtools.yang.model.api.ChoiceNode.class);
507             potential = findChoice(choices, child);
508         }
509
510         if (potential == null) {
511             throw new DataNormalizationException(String.format("Supplied QName %s is not valid according to schema %s", child, schema));
512         }
513
514         if ((schema instanceof DataSchemaNode) && !((DataSchemaNode) schema).isAugmenting() && potential.isAugmenting()) {
515             return fromAugmentation(schema, (AugmentationTarget) schema, potential);
516         }
517         return fromDataSchemaNode(potential);
518     }
519
520     private static org.opendaylight.yangtools.yang.model.api.ChoiceNode findChoice(
521             final Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode> choices, final QName child) {
522         org.opendaylight.yangtools.yang.model.api.ChoiceNode foundChoice = null;
523         choiceLoop: for (org.opendaylight.yangtools.yang.model.api.ChoiceNode choice : choices) {
524             for (ChoiceCaseNode caze : choice.getCases()) {
525                 if (caze.getDataChildByName(child) != null) {
526                     foundChoice = choice;
527                     break choiceLoop;
528                 }
529             }
530         }
531         return foundChoice;
532     }
533
534     public static AugmentationIdentifier augmentationIdentifierFrom(final AugmentationSchema augmentation) {
535         ImmutableSet.Builder<QName> potentialChildren = ImmutableSet.builder();
536         for (DataSchemaNode child : augmentation.getChildNodes()) {
537             potentialChildren.add(child.getQName());
538         }
539         return new AugmentationIdentifier(null, potentialChildren.build());
540     }
541
542     private static AugmentationNormalization fromAugmentation(final DataNodeContainer schema,
543             final AugmentationTarget augments, final DataSchemaNode potential) {
544         AugmentationSchema augmentation = null;
545         for (AugmentationSchema aug : augments.getAvailableAugmentations()) {
546             DataSchemaNode child = aug.getDataChildByName(potential.getQName());
547             if (child != null) {
548                 augmentation = aug;
549                 break;
550             }
551
552         }
553         if (augmentation != null) {
554             return new AugmentationNormalization(augmentation, schema);
555         } else {
556             return null;
557         }
558     }
559
560     private static DataNormalizationOperation<?> fromSchema(final DataNodeContainer schema, final PathArgument child) throws DataNormalizationException {
561         if (child instanceof AugmentationIdentifier) {
562             return fromSchemaAndPathArgument(schema, ((AugmentationIdentifier) child).getPossibleChildNames()
563                     .iterator().next());
564         }
565         return fromSchemaAndPathArgument(schema, child.getNodeType());
566     }
567
568     public static DataNormalizationOperation<?> fromDataSchemaNode(final DataSchemaNode potential) {
569         if (potential instanceof ContainerSchemaNode) {
570             return new ContainerNormalization((ContainerSchemaNode) potential);
571         } else if (potential instanceof ListSchemaNode) {
572             return new ListMixinNormalization((ListSchemaNode) potential);
573         } else if (potential instanceof LeafSchemaNode) {
574             return new LeafNormalization(new NodeIdentifier(potential.getQName()));
575         } else if (potential instanceof org.opendaylight.yangtools.yang.model.api.ChoiceNode) {
576             return new ChoiceNodeNormalization((org.opendaylight.yangtools.yang.model.api.ChoiceNode) potential);
577         } else if (potential instanceof LeafListSchemaNode) {
578             return new LeafListMixinNormalization((LeafListSchemaNode) potential);
579         }
580         return null;
581     }
582
583     public static DataNormalizationOperation<?> from(final SchemaContext ctx) {
584         return new ContainerNormalization(ctx);
585     }
586
587     public abstract NormalizedNode<?, ?> createDefault(PathArgument currentArg);
588 }