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