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