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