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