19ccfe24e302c4e4204a5e323d14efa5c64bc727
[netconf.git] / restconf / restconf-nb-bierman02 / src / main / java / org / opendaylight / netconf / sal / restconf / impl / 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.netconf.sal.restconf.impl;
9
10 import com.google.common.collect.ImmutableMap;
11 import com.google.common.collect.ImmutableSet;
12 import com.google.common.collect.Iterables;
13 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
14 import java.util.Collections;
15 import java.util.HashSet;
16 import java.util.Map;
17 import java.util.Set;
18 import java.util.concurrent.ConcurrentHashMap;
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.opendaylight.yangtools.concepts.Identifiable;
21 import org.opendaylight.yangtools.yang.common.QName;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
27 import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
30 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.ContainerLike;
33 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
35 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
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.util.EffectiveAugmentationSchema;
41
42 abstract class DataNormalizationOperation<T extends PathArgument> implements Identifiable<T> {
43     private final T identifier;
44
45     @Override
46     public T getIdentifier() {
47         return identifier;
48     }
49
50     DataNormalizationOperation(final T identifier) {
51         this.identifier = identifier;
52     }
53
54     static DataNormalizationOperation<?> from(final EffectiveModelContext ctx) {
55         return new ContainerNormalization(ctx);
56     }
57
58     boolean isMixin() {
59         return false;
60     }
61
62     Set<QName> getQNameIdentifiers() {
63         return Collections.singleton(identifier.getNodeType());
64     }
65
66     abstract DataNormalizationOperation<?> getChild(PathArgument child) throws DataNormalizationException;
67
68     abstract DataNormalizationOperation<?> getChild(QName child) throws DataNormalizationException;
69
70     private abstract static class SimpleTypeNormalization<T extends PathArgument>
71             extends DataNormalizationOperation<T> {
72         SimpleTypeNormalization(final T identifier) {
73             super(identifier);
74         }
75
76         @Override
77         final DataNormalizationOperation<?> getChild(final PathArgument child) {
78             return null;
79         }
80
81         @Override
82         final DataNormalizationOperation<?> getChild(final QName child) {
83             return null;
84         }
85     }
86
87     private static final class LeafNormalization extends SimpleTypeNormalization<NodeIdentifier> {
88         LeafNormalization(final LeafSchemaNode potential) {
89             super(new NodeIdentifier(potential.getQName()));
90         }
91     }
92
93     private static final class LeafListEntryNormalization extends SimpleTypeNormalization<NodeWithValue> {
94         LeafListEntryNormalization(final LeafListSchemaNode potential) {
95             super(new NodeWithValue(potential.getQName(), null));
96         }
97     }
98
99     private abstract static class DataContainerNormalizationOperation<T extends PathArgument>
100             extends DataNormalizationOperation<T> {
101         private final DataNodeContainer schema;
102         private final Map<QName, DataNormalizationOperation<?>> byQName;
103         private final Map<PathArgument, DataNormalizationOperation<?>> byArg;
104
105         DataContainerNormalizationOperation(final T identifier, final DataNodeContainer schema) {
106             super(identifier);
107             this.schema = schema;
108             this.byArg = new ConcurrentHashMap<>();
109             this.byQName = new ConcurrentHashMap<>();
110         }
111
112         @Override
113         DataNormalizationOperation<?> getChild(final PathArgument child) throws DataNormalizationException {
114             DataNormalizationOperation<?> potential = byArg.get(child);
115             if (potential != null) {
116                 return potential;
117             }
118             potential = fromLocalSchema(child);
119             return register(potential);
120         }
121
122         @Override
123         DataNormalizationOperation<?> getChild(final QName child) throws DataNormalizationException {
124             DataNormalizationOperation<?> potential = byQName.get(child);
125             if (potential != null) {
126                 return potential;
127             }
128             potential = fromLocalSchemaAndQName(schema, child);
129             return register(potential);
130         }
131
132         private DataNormalizationOperation<?> fromLocalSchema(final PathArgument child)
133                 throws DataNormalizationException {
134             if (child instanceof AugmentationIdentifier) {
135                 return fromSchemaAndQNameChecked(schema, ((AugmentationIdentifier) child).getPossibleChildNames()
136                         .iterator().next());
137             }
138             return fromSchemaAndQNameChecked(schema, child.getNodeType());
139         }
140
141         DataNormalizationOperation<?> fromLocalSchemaAndQName(final DataNodeContainer schema2,
142                 final QName child) throws DataNormalizationException {
143             return fromSchemaAndQNameChecked(schema2, child);
144         }
145
146         private DataNormalizationOperation<?> register(final DataNormalizationOperation<?> potential) {
147             if (potential != null) {
148                 byArg.put(potential.getIdentifier(), potential);
149                 for (final QName qname : potential.getQNameIdentifiers()) {
150                     byQName.put(qname, potential);
151                 }
152             }
153             return potential;
154         }
155
156         private static DataNormalizationOperation<?> fromSchemaAndQNameChecked(final DataNodeContainer schema,
157                 final QName child) throws DataNormalizationException {
158
159             final DataSchemaNode result = findChildSchemaNode(schema, child);
160             if (result == null) {
161                 throw new DataNormalizationException(String.format(
162                         "Supplied QName %s is not valid according to schema %s, potential children nodes: %s", child,
163                         schema,schema.getChildNodes()));
164             }
165
166             // We try to look up if this node was added by augmentation
167             if (schema instanceof DataSchemaNode && result.isAugmenting()) {
168                 return fromAugmentation(schema, (AugmentationTarget) schema, result);
169             }
170             return fromDataSchemaNode(result);
171         }
172     }
173
174     private static final class ListItemNormalization extends
175             DataContainerNormalizationOperation<NodeIdentifierWithPredicates> {
176         ListItemNormalization(final NodeIdentifierWithPredicates identifier, final ListSchemaNode schema) {
177             super(identifier, schema);
178         }
179     }
180
181     private static final class UnkeyedListItemNormalization
182             extends DataContainerNormalizationOperation<NodeIdentifier> {
183         UnkeyedListItemNormalization(final ListSchemaNode schema) {
184             super(new NodeIdentifier(schema.getQName()), schema);
185         }
186     }
187
188     private static final class ContainerNormalization extends DataContainerNormalizationOperation<NodeIdentifier> {
189         ContainerNormalization(final ContainerLike schema) {
190             super(new NodeIdentifier(schema.getQName()), schema);
191         }
192     }
193
194     private abstract static class MixinNormalizationOp<T extends PathArgument> extends DataNormalizationOperation<T> {
195         MixinNormalizationOp(final T identifier) {
196             super(identifier);
197         }
198
199         @Override
200         final boolean isMixin() {
201             return true;
202         }
203     }
204
205     private static final class LeafListMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
206         private final DataNormalizationOperation<?> innerOp;
207
208         LeafListMixinNormalization(final LeafListSchemaNode potential) {
209             super(new NodeIdentifier(potential.getQName()));
210             innerOp = new LeafListEntryNormalization(potential);
211         }
212
213         @Override
214         DataNormalizationOperation<?> getChild(final PathArgument child) {
215             if (child instanceof NodeWithValue) {
216                 return innerOp;
217             }
218             return null;
219         }
220
221         @Override
222         DataNormalizationOperation<?> getChild(final QName child) {
223             if (getIdentifier().getNodeType().equals(child)) {
224                 return innerOp;
225             }
226             return null;
227         }
228     }
229
230     private static final class AugmentationNormalization
231             extends DataContainerNormalizationOperation<AugmentationIdentifier> {
232
233         AugmentationNormalization(final AugmentationSchemaNode augmentation, final DataNodeContainer schema) {
234             super(augmentationIdentifierFrom(augmentation), augmentationProxy(augmentation,schema));
235         }
236
237         private static DataNodeContainer augmentationProxy(final AugmentationSchemaNode augmentation,
238                 final DataNodeContainer schema) {
239             final Set<DataSchemaNode> children = new HashSet<>();
240             for (final DataSchemaNode augNode : augmentation.getChildNodes()) {
241                 children.add(schema.getDataChildByName(augNode.getQName()));
242             }
243             return new EffectiveAugmentationSchema(augmentation, children);
244         }
245
246         @Override
247         boolean isMixin() {
248             return true;
249         }
250
251         @Override
252         DataNormalizationOperation<?> fromLocalSchemaAndQName(final DataNodeContainer schema, final QName child) {
253             final DataSchemaNode result = findChildSchemaNode(schema, child);
254             if (result == null) {
255                 return null;
256             }
257
258             // We try to look up if this node was added by augmentation
259             if (schema instanceof DataSchemaNode && result.isAugmenting()) {
260                 return fromAugmentation(schema, (AugmentationTarget) schema, result);
261             }
262             return fromDataSchemaNode(result);
263         }
264
265         @Override
266         Set<QName> getQNameIdentifiers() {
267             return getIdentifier().getPossibleChildNames();
268         }
269
270         private static AugmentationIdentifier augmentationIdentifierFrom(final AugmentationSchemaNode augmentation) {
271             final ImmutableSet.Builder<QName> potentialChildren = ImmutableSet.builder();
272             for (final DataSchemaNode child : augmentation.getChildNodes()) {
273                 potentialChildren.add(child.getQName());
274             }
275             return new AugmentationIdentifier(potentialChildren.build());
276         }
277     }
278
279     private static final class MapMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
280         private final ListItemNormalization innerNode;
281
282         MapMixinNormalization(final ListSchemaNode list) {
283             super(new NodeIdentifier(list.getQName()));
284             this.innerNode = new ListItemNormalization(NodeIdentifierWithPredicates.of(list.getQName(),
285                     Collections.<QName, Object>emptyMap()), list);
286         }
287
288         @Override
289         DataNormalizationOperation<?> getChild(final PathArgument child) {
290             if (child.getNodeType().equals(getIdentifier().getNodeType())) {
291                 return innerNode;
292             }
293             return null;
294         }
295
296         @Override
297         DataNormalizationOperation<?> getChild(final QName child) {
298             if (getIdentifier().getNodeType().equals(child)) {
299                 return innerNode;
300             }
301             return null;
302         }
303     }
304
305     private static final class UnkeyedListMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
306         private final UnkeyedListItemNormalization innerNode;
307
308         UnkeyedListMixinNormalization(final ListSchemaNode list) {
309             super(new NodeIdentifier(list.getQName()));
310             this.innerNode = new UnkeyedListItemNormalization(list);
311         }
312
313         @Override
314         DataNormalizationOperation<?> getChild(final PathArgument child) {
315             if (child.getNodeType().equals(getIdentifier().getNodeType())) {
316                 return innerNode;
317             }
318             return null;
319         }
320
321         @Override
322         DataNormalizationOperation<?> getChild(final QName child) {
323             if (getIdentifier().getNodeType().equals(child)) {
324                 return innerNode;
325             }
326             return null;
327         }
328     }
329
330     private static final class ChoiceNodeNormalization extends MixinNormalizationOp<NodeIdentifier> {
331         private final ImmutableMap<QName, DataNormalizationOperation<?>> byQName;
332         private final ImmutableMap<PathArgument, DataNormalizationOperation<?>> byArg;
333
334         ChoiceNodeNormalization(final ChoiceSchemaNode schema) {
335             super(new NodeIdentifier(schema.getQName()));
336             final ImmutableMap.Builder<QName, DataNormalizationOperation<?>> byQNameBuilder = ImmutableMap.builder();
337             final ImmutableMap.Builder<PathArgument, DataNormalizationOperation<?>> byArgBuilder =
338                     ImmutableMap.builder();
339
340             for (final CaseSchemaNode caze : schema.getCases()) {
341                 for (final DataSchemaNode cazeChild : caze.getChildNodes()) {
342                     final DataNormalizationOperation<?> childOp = fromDataSchemaNode(cazeChild);
343                     byArgBuilder.put(childOp.getIdentifier(), childOp);
344                     for (final QName qname : childOp.getQNameIdentifiers()) {
345                         byQNameBuilder.put(qname, childOp);
346                     }
347                 }
348             }
349             byQName = byQNameBuilder.build();
350             byArg = byArgBuilder.build();
351         }
352
353         @Override
354         DataNormalizationOperation<?> getChild(final PathArgument child) {
355             return byArg.get(child);
356         }
357
358         @Override
359         DataNormalizationOperation<?> getChild(final QName child) {
360             return byQName.get(child);
361         }
362     }
363
364     private static final class AnyxmlNormalization extends SimpleTypeNormalization<NodeIdentifier> {
365         AnyxmlNormalization(final AnyxmlSchemaNode schema) {
366             super(new NodeIdentifier(schema.getQName()));
367         }
368     }
369
370     private static @Nullable DataSchemaNode findChildSchemaNode(final DataNodeContainer parent, final QName child) {
371         final DataSchemaNode potential = parent.getDataChildByName(child);
372         return potential != null ? potential : findChoice(parent, child);
373     }
374
375     private static @Nullable ChoiceSchemaNode findChoice(final DataNodeContainer parent, final QName child) {
376         for (final ChoiceSchemaNode choice : Iterables.filter(parent.getChildNodes(), ChoiceSchemaNode.class)) {
377             for (final CaseSchemaNode caze : choice.getCases()) {
378                 if (findChildSchemaNode(caze, child) != null) {
379                     return choice;
380                 }
381             }
382         }
383         return null;
384     }
385
386     /**
387      * Returns a DataNormalizationOperation for provided child node.
388      *
389      * <p>
390      * If supplied child is added by Augmentation this operation returns
391      * a DataNormalizationOperation for augmentation,
392      * otherwise returns a DataNormalizationOperation for child as
393      * call for {@link #fromDataSchemaNode(DataSchemaNode)}.
394      */
395     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
396             justification = "https://github.com/spotbugs/spotbugs/issues/811")
397     private static DataNormalizationOperation<?> fromAugmentation(final DataNodeContainer parent,
398             final AugmentationTarget parentAug, final DataSchemaNode child) {
399         AugmentationSchemaNode augmentation = null;
400         for (final AugmentationSchemaNode aug : parentAug.getAvailableAugmentations()) {
401             final DataSchemaNode potential = aug.getDataChildByName(child.getQName());
402             if (potential != null) {
403                 augmentation = aug;
404                 break;
405             }
406
407         }
408         if (augmentation != null) {
409             return new AugmentationNormalization(augmentation, parent);
410         } else {
411             return fromDataSchemaNode(child);
412         }
413     }
414
415     static DataNormalizationOperation<?> fromDataSchemaNode(final DataSchemaNode potential) {
416         if (potential instanceof ContainerSchemaNode) {
417             return new ContainerNormalization((ContainerSchemaNode) potential);
418         } else if (potential instanceof ListSchemaNode) {
419             return fromListSchemaNode((ListSchemaNode) potential);
420         } else if (potential instanceof LeafSchemaNode) {
421             return new LeafNormalization((LeafSchemaNode) potential);
422         } else if (potential instanceof ChoiceSchemaNode) {
423             return new ChoiceNodeNormalization((ChoiceSchemaNode) potential);
424         } else if (potential instanceof LeafListSchemaNode) {
425             return new LeafListMixinNormalization((LeafListSchemaNode) potential);
426         } else if (potential instanceof AnyxmlSchemaNode) {
427             return new AnyxmlNormalization((AnyxmlSchemaNode) potential);
428         }
429         return null;
430     }
431
432     private static DataNormalizationOperation<?> fromListSchemaNode(final ListSchemaNode potential) {
433         if (potential.getKeyDefinition().isEmpty()) {
434             return new UnkeyedListMixinNormalization(potential);
435         }
436         return new MapMixinNormalization(potential);
437     }
438 }