Merge "BUG-2288: deprecate old binding Notification API elements"
[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 com.google.common.base.Optional;
11 import com.google.common.collect.FluentIterable;
12 import com.google.common.collect.ImmutableMap;
13 import com.google.common.collect.ImmutableSet;
14 import java.util.Collections;
15 import java.util.HashSet;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Map.Entry;
19 import java.util.Set;
20 import java.util.concurrent.ConcurrentHashMap;
21 import org.opendaylight.yangtools.concepts.Identifiable;
22 import org.opendaylight.yangtools.yang.common.QName;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
28 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
29 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
30 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
31 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
32 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
34 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
35 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
36 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
39 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
43 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
44 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
45
46 @Deprecated
47 public abstract class DataNormalizationOperation<T extends PathArgument> implements Identifiable<T> {
48
49     private final T identifier;
50     private final Optional<DataSchemaNode> dataSchemaNode;
51
52     @Override
53     public T getIdentifier() {
54         return identifier;
55     };
56
57     protected DataNormalizationOperation(final T identifier, final SchemaNode schema) {
58         super();
59         this.identifier = identifier;
60         if(schema instanceof DataSchemaNode) {
61             this.dataSchemaNode = Optional.of((DataSchemaNode) schema);
62         } else {
63             this.dataSchemaNode = Optional.absent();
64         }
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
85     public abstract boolean isLeaf();
86
87     public Optional<DataSchemaNode> getDataSchemaNode() {
88         // FIXME
89         return dataSchemaNode;
90     }
91
92     private static abstract class SimpleTypeNormalization<T extends PathArgument> extends DataNormalizationOperation<T> {
93
94         protected SimpleTypeNormalization(final T identifier, final DataSchemaNode potential) {
95             super(identifier,potential);
96         }
97
98         @Override
99         public DataNormalizationOperation<?> getChild(final PathArgument child) {
100             return null;
101         }
102
103         @Override
104         public DataNormalizationOperation<?> getChild(final QName child) {
105             return null;
106         }
107
108         @Override
109         public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
110             return null;
111         }
112
113         @Override
114         public boolean isLeaf() {
115             return true;
116         }
117
118     }
119
120     private static final class LeafNormalization extends SimpleTypeNormalization<NodeIdentifier> {
121
122         protected LeafNormalization(final LeafSchemaNode potential) {
123             super(new NodeIdentifier(potential.getQName()),potential);
124         }
125
126     }
127
128     private static final class LeafListEntryNormalization extends SimpleTypeNormalization<NodeWithValue> {
129
130         public LeafListEntryNormalization(final LeafListSchemaNode potential) {
131             super(new NodeWithValue(potential.getQName(), null),potential);
132         }
133
134         @Override
135         public boolean isKeyedEntry() {
136             return true;
137         }
138     }
139
140     private static abstract class CompositeNodeNormalizationOperation<T extends PathArgument> extends
141     DataNormalizationOperation<T> {
142
143         protected CompositeNodeNormalizationOperation(final T identifier, final DataSchemaNode schema) {
144             super(identifier,schema);
145         }
146
147         @Override
148         public boolean isLeaf() {
149             return false;
150         }
151
152
153     }
154
155     private static abstract class DataContainerNormalizationOperation<T extends PathArgument> extends
156     CompositeNodeNormalizationOperation<T> {
157
158         private final DataNodeContainer schema;
159         private final Map<QName, DataNormalizationOperation<?>> byQName;
160         private final Map<PathArgument, DataNormalizationOperation<?>> byArg;
161
162         protected DataContainerNormalizationOperation(final T identifier, final DataNodeContainer schema, final DataSchemaNode node) {
163             super(identifier,node);
164             this.schema = schema;
165             this.byArg = new ConcurrentHashMap<>();
166             this.byQName = new ConcurrentHashMap<>();
167         }
168
169         @Override
170         public DataNormalizationOperation<?> getChild(final PathArgument child) throws DataNormalizationException {
171             DataNormalizationOperation<?> potential = byArg.get(child);
172             if (potential != null) {
173                 return potential;
174             }
175             potential = fromLocalSchema(child);
176             return register(potential);
177         }
178
179         private DataNormalizationOperation<?> fromLocalSchema(final PathArgument child) throws DataNormalizationException {
180             if (child instanceof AugmentationIdentifier) {
181                 return fromSchemaAndQNameChecked(schema, ((AugmentationIdentifier) child).getPossibleChildNames()
182                         .iterator().next());
183             }
184             return fromSchemaAndQNameChecked(schema, child.getNodeType());
185         }
186
187         @Override
188         public DataNormalizationOperation<?> getChild(final QName child) throws DataNormalizationException {
189             DataNormalizationOperation<?> potential = byQName.get(child);
190             if (potential != null) {
191                 return potential;
192             }
193             potential = fromLocalSchemaAndQName(schema, child);
194             return register(potential);
195         }
196
197         protected DataNormalizationOperation<?> fromLocalSchemaAndQName(final DataNodeContainer schema2, final QName child) throws DataNormalizationException {
198             return fromSchemaAndQNameChecked(schema2, child);
199         }
200
201         private DataNormalizationOperation<?> register(final DataNormalizationOperation<?> potential) {
202             if (potential != null) {
203                 byArg.put(potential.getIdentifier(), potential);
204                 for (QName qName : potential.getQNameIdentifiers()) {
205                     byQName.put(qName, potential);
206                 }
207             }
208             return potential;
209         }
210
211     }
212
213     private static final class ListItemNormalization extends
214     DataContainerNormalizationOperation<NodeIdentifierWithPredicates> {
215
216         private final List<QName> keyDefinition;
217
218         protected ListItemNormalization(final NodeIdentifierWithPredicates identifier, final ListSchemaNode schema) {
219             super(identifier, schema,schema);
220             keyDefinition = schema.getKeyDefinition();
221         }
222
223         @Override
224         public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
225             DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = Builders
226                     .mapEntryBuilder().withNodeIdentifier((NodeIdentifierWithPredicates) currentArg);
227             for (Entry<QName, Object> keyValue : ((NodeIdentifierWithPredicates) currentArg).getKeyValues().entrySet()) {
228                 builder.addChild(Builders.leafBuilder()
229                         //
230                         .withNodeIdentifier(new NodeIdentifier(keyValue.getKey())).withValue(keyValue.getValue())
231                         .build());
232             }
233             return builder.build();
234         }
235
236
237         @Override
238         public boolean isKeyedEntry() {
239             return true;
240         }
241     }
242
243     private static final class UnkeyedListItemNormalization extends DataContainerNormalizationOperation<NodeIdentifier> {
244
245         protected UnkeyedListItemNormalization(final ListSchemaNode schema) {
246             super(new NodeIdentifier(schema.getQName()), schema,schema);
247         }
248
249         @Override
250         public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
251             return Builders.unkeyedListEntryBuilder().withNodeIdentifier((NodeIdentifier) currentArg).build();
252         }
253
254     }
255
256     private static final class ContainerNormalization extends DataContainerNormalizationOperation<NodeIdentifier> {
257
258         protected ContainerNormalization(final ContainerSchemaNode schema) {
259             super(new NodeIdentifier(schema.getQName()),schema, schema);
260         }
261
262         @Override
263         public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
264             return Builders.containerBuilder().withNodeIdentifier((NodeIdentifier) currentArg).build();
265         }
266
267     }
268
269     private static abstract class MixinNormalizationOp<T extends PathArgument> extends
270     CompositeNodeNormalizationOperation<T> {
271
272         protected MixinNormalizationOp(final T identifier, final DataSchemaNode schema) {
273             super(identifier,schema);
274         }
275
276         @Override
277         public final boolean isMixin() {
278             return true;
279         }
280
281     }
282
283
284     private static final class OrderedLeafListMixinNormalization extends UnorderedLeafListMixinNormalization {
285
286
287         public OrderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
288             super(potential);
289         }
290
291         @Override
292         public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
293             return Builders.orderedLeafSetBuilder().withNodeIdentifier(getIdentifier()).build();
294         }
295     }
296
297     private static class UnorderedLeafListMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
298
299         private final DataNormalizationOperation<?> innerOp;
300
301         public UnorderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
302             super(new NodeIdentifier(potential.getQName()),potential);
303             innerOp = new LeafListEntryNormalization(potential);
304         }
305         @Override
306         public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
307             return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier()).build();
308         }
309
310         @Override
311         public DataNormalizationOperation<?> getChild(final PathArgument child) {
312             if (child instanceof NodeWithValue) {
313                 return innerOp;
314             }
315             return null;
316         }
317
318         @Override
319         public DataNormalizationOperation<?> getChild(final QName child) {
320             if (getIdentifier().getNodeType().equals(child)) {
321                 return innerOp;
322             }
323             return null;
324         }
325     }
326
327     private static final class AugmentationNormalization extends DataContainerNormalizationOperation<AugmentationIdentifier> {
328
329         public AugmentationNormalization(final AugmentationSchema augmentation, final DataNodeContainer schema) {
330             //super();
331             super(augmentationIdentifierFrom(augmentation), augmentationProxy(augmentation,schema),null);
332         }
333
334         @Override
335         public boolean isMixin() {
336             return true;
337         }
338
339
340
341         @Override
342         protected DataNormalizationOperation<?> fromLocalSchemaAndQName(final DataNodeContainer schema, final QName child)
343                 throws DataNormalizationException {
344             Optional<DataSchemaNode> potential = findChildSchemaNode(schema, child);
345             if (!potential.isPresent()) {
346                 return null;
347             }
348
349             DataSchemaNode result = potential.get();
350             // We try to look up if this node was added by augmentation
351             if ((schema instanceof DataSchemaNode) && result.isAugmenting()) {
352                 return fromAugmentation(schema, (AugmentationTarget) schema, result);
353             }
354             return fromDataSchemaNode(result);
355         }
356
357         @Override
358         protected Set<QName> getQNameIdentifiers() {
359             return getIdentifier().getPossibleChildNames();
360         }
361
362         @Override
363         public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
364             return Builders.augmentationBuilder().withNodeIdentifier(getIdentifier()).build();
365         }
366
367     }
368
369     private static class UnorderedMapMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
370
371         private final ListItemNormalization innerNode;
372
373         public UnorderedMapMixinNormalization(final ListSchemaNode list) {
374             super(new NodeIdentifier(list.getQName()),list);
375             this.innerNode = new ListItemNormalization(new NodeIdentifierWithPredicates(list.getQName(),
376                     Collections.<QName, Object> emptyMap()), list);
377         }
378
379         @Override
380         public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
381             return Builders.mapBuilder().withNodeIdentifier(getIdentifier()).build();
382         }
383
384         @Override
385         public DataNormalizationOperation<?> getChild(final PathArgument child) {
386             if (child.getNodeType().equals(getIdentifier().getNodeType())) {
387                 return innerNode;
388             }
389             return null;
390         }
391
392         @Override
393         public DataNormalizationOperation<?> getChild(final QName child) {
394             if (getIdentifier().getNodeType().equals(child)) {
395                 return innerNode;
396             }
397             return null;
398         }
399
400     }
401
402
403     private static class UnkeyedListMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
404
405         private final UnkeyedListItemNormalization innerNode;
406
407         public UnkeyedListMixinNormalization(final ListSchemaNode list) {
408             super(new NodeIdentifier(list.getQName()),list);
409             this.innerNode = new UnkeyedListItemNormalization(list);
410         }
411
412         @Override
413         public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
414             return Builders.unkeyedListBuilder().withNodeIdentifier(getIdentifier()).build();
415         }
416
417         @Override
418         public DataNormalizationOperation<?> getChild(final PathArgument child) {
419             if (child.getNodeType().equals(getIdentifier().getNodeType())) {
420                 return innerNode;
421             }
422             return null;
423         }
424
425         @Override
426         public DataNormalizationOperation<?> getChild(final QName child) {
427             if (getIdentifier().getNodeType().equals(child)) {
428                 return innerNode;
429             }
430             return null;
431         }
432
433     }
434
435     private static final class OrderedMapMixinNormalization extends UnorderedMapMixinNormalization {
436
437         public OrderedMapMixinNormalization(final ListSchemaNode list) {
438             super(list);
439         }
440
441         @Override
442         public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
443             return Builders.orderedMapBuilder().withNodeIdentifier(getIdentifier()).build();
444         }
445
446     }
447
448     private static class ChoiceNodeNormalization extends MixinNormalizationOp<NodeIdentifier> {
449
450         private final ImmutableMap<QName, DataNormalizationOperation<?>> byQName;
451         private final ImmutableMap<PathArgument, DataNormalizationOperation<?>> byArg;
452
453         protected ChoiceNodeNormalization(final ChoiceSchemaNode schema) {
454             super(new NodeIdentifier(schema.getQName()),schema);
455             ImmutableMap.Builder<QName, DataNormalizationOperation<?>> byQNameBuilder = ImmutableMap.builder();
456             ImmutableMap.Builder<PathArgument, DataNormalizationOperation<?>> byArgBuilder = ImmutableMap.builder();
457
458             for (ChoiceCaseNode caze : schema.getCases()) {
459                 for (DataSchemaNode cazeChild : caze.getChildNodes()) {
460                     DataNormalizationOperation<?> childOp = fromDataSchemaNode(cazeChild);
461                     byArgBuilder.put(childOp.getIdentifier(), childOp);
462                     for (QName qname : childOp.getQNameIdentifiers()) {
463                         byQNameBuilder.put(qname, childOp);
464                     }
465                 }
466             }
467             byQName = byQNameBuilder.build();
468             byArg = byArgBuilder.build();
469         }
470
471         @Override
472         public DataNormalizationOperation<?> getChild(final PathArgument child) {
473             return byArg.get(child);
474         }
475
476         @Override
477         public DataNormalizationOperation<?> getChild(final QName child) {
478             return byQName.get(child);
479         }
480
481         @Override
482         public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
483             return Builders.choiceBuilder().withNodeIdentifier(getIdentifier()).build();
484         }
485     }
486
487     private static class AnyXmlNormalization extends DataNormalizationOperation<NodeIdentifier> {
488
489         protected AnyXmlNormalization( final AnyXmlSchemaNode schema) {
490             super( new NodeIdentifier(schema.getQName()), schema);
491         }
492
493         @Override
494         public DataNormalizationOperation<?> getChild( final PathArgument child ) throws DataNormalizationException {
495             return null;
496         }
497
498         @Override
499         public DataNormalizationOperation<?> getChild( final QName child ) throws DataNormalizationException {
500             return null;
501         }
502
503         @Override
504         public boolean isLeaf() {
505             return false;
506         }
507
508         @Override
509         public NormalizedNode<?, ?> createDefault( final PathArgument currentArg ) {
510             return null;
511         }
512     }
513
514     private static final Optional<DataSchemaNode> findChildSchemaNode(final DataNodeContainer parent,final QName child) {
515         DataSchemaNode potential = parent.getDataChildByName(child);
516         if (potential == null) {
517             Iterable<ChoiceSchemaNode> choices = FluentIterable.from(parent.getChildNodes()).filter(ChoiceSchemaNode.class);
518             potential = findChoice(choices, child);
519         }
520         return Optional.fromNullable(potential);
521     }
522
523     private static DataNormalizationOperation<?> fromSchemaAndQNameChecked(final DataNodeContainer schema,
524             final QName child) throws DataNormalizationException {
525
526         Optional<DataSchemaNode> potential = findChildSchemaNode(schema, child);
527         if (!potential.isPresent()) {
528             throw new DataNormalizationException(String.format("Supplied QName %s is not valid according to schema %s, potential children nodes: %s", child, schema,schema.getChildNodes()));
529         }
530
531         DataSchemaNode result = potential.get();
532         // We try to look up if this node was added by augmentation
533         if ((schema instanceof DataSchemaNode) && result.isAugmenting()) {
534             return fromAugmentation(schema, (AugmentationTarget) schema, result);
535         }
536         return fromDataSchemaNode(result);
537     }
538
539     private static ChoiceSchemaNode findChoice(final Iterable<ChoiceSchemaNode> choices, final QName child) {
540         ChoiceSchemaNode foundChoice = null;
541         choiceLoop: for (ChoiceSchemaNode choice : choices) {
542             for (ChoiceCaseNode caze : choice.getCases()) {
543                 if (findChildSchemaNode(caze, child).isPresent()) {
544                     foundChoice = choice;
545                     break choiceLoop;
546                 }
547             }
548         }
549         return foundChoice;
550     }
551
552     public static AugmentationIdentifier augmentationIdentifierFrom(final AugmentationSchema augmentation) {
553         ImmutableSet.Builder<QName> potentialChildren = ImmutableSet.builder();
554         for (DataSchemaNode child : augmentation.getChildNodes()) {
555             potentialChildren.add(child.getQName());
556         }
557         return new AugmentationIdentifier(potentialChildren.build());
558     }
559
560     private static DataNodeContainer augmentationProxy(final AugmentationSchema augmentation, final DataNodeContainer schema) {
561         Set<DataSchemaNode> children = new HashSet<>();
562         for (DataSchemaNode augNode : augmentation.getChildNodes()) {
563             children.add(schema.getDataChildByName(augNode.getQName()));
564         }
565         return new DataSchemaContainerProxy(children);
566     }
567
568     /**
569      * Returns a DataNormalizationOperation for provided child node
570      *
571      * If supplied child is added by Augmentation this operation returns
572      * a DataNormalizationOperation for augmentation,
573      * otherwise returns a DataNormalizationOperation for child as
574      * call for {@link #fromDataSchemaNode(DataSchemaNode)}.
575      *
576      *
577      * @param parent
578      * @param parentAug
579      * @param child
580      * @return
581      */
582     private static DataNormalizationOperation<?> fromAugmentation(final DataNodeContainer parent,
583             final AugmentationTarget parentAug, final DataSchemaNode child) {
584         AugmentationSchema augmentation = null;
585         for (AugmentationSchema aug : parentAug.getAvailableAugmentations()) {
586             DataSchemaNode potential = aug.getDataChildByName(child.getQName());
587             if (potential != null) {
588                 augmentation = aug;
589                 break;
590             }
591
592         }
593         if (augmentation != null) {
594             return new AugmentationNormalization(augmentation, parent);
595         } else {
596             return fromDataSchemaNode(child);
597         }
598     }
599
600     public static DataNormalizationOperation<?> fromDataSchemaNode(final DataSchemaNode potential) {
601         if (potential instanceof ContainerSchemaNode) {
602             return new ContainerNormalization((ContainerSchemaNode) potential);
603         } else if (potential instanceof ListSchemaNode) {
604
605             return fromListSchemaNode((ListSchemaNode) potential);
606         } else if (potential instanceof LeafSchemaNode) {
607             return new LeafNormalization((LeafSchemaNode) potential);
608         } else if (potential instanceof ChoiceSchemaNode) {
609             return new ChoiceNodeNormalization((ChoiceSchemaNode) potential);
610         } else if (potential instanceof LeafListSchemaNode) {
611             return fromLeafListSchemaNode((LeafListSchemaNode) potential);
612         } else if (potential instanceof AnyXmlSchemaNode) {
613             return new AnyXmlNormalization( (AnyXmlSchemaNode) potential);
614         }
615         return null;
616     }
617
618     private static DataNormalizationOperation<?> fromListSchemaNode(final ListSchemaNode potential) {
619         List<QName> keyDefinition = potential.getKeyDefinition();
620         if(keyDefinition == null || keyDefinition.isEmpty()) {
621             return new UnkeyedListMixinNormalization(potential);
622         }
623         if(potential.isUserOrdered()) {
624             return new OrderedMapMixinNormalization(potential);
625         }
626         return new UnorderedMapMixinNormalization(potential);
627     }
628
629     private static DataNormalizationOperation<?> fromLeafListSchemaNode(final LeafListSchemaNode potential) {
630         if(potential.isUserOrdered()) {
631             return new OrderedLeafListMixinNormalization(potential);
632         }
633         return new UnorderedLeafListMixinNormalization(potential);
634     }
635
636
637     public static DataNormalizationOperation<?> from(final SchemaContext ctx) {
638         return new ContainerNormalization(ctx);
639     }
640
641     public abstract NormalizedNode<?, ?> createDefault(PathArgument currentArg);
642 }