Remove duplicate instantiation of EffectiveAugmentationSchema
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / schema / InstanceIdToCompositeNodes.java
1 /*
2  * Copyright (c) 2015 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.yangtools.yang.data.impl.schema;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Preconditions.checkNotNull;
12
13 import com.google.common.collect.ImmutableList;
14 import com.google.common.collect.ImmutableMap;
15 import com.google.common.collect.ImmutableSet;
16 import com.google.common.collect.Iterables;
17 import java.util.Collection;
18 import java.util.Collections;
19 import java.util.Iterator;
20 import java.util.Map;
21 import java.util.Map.Entry;
22 import java.util.Optional;
23 import java.util.concurrent.ConcurrentHashMap;
24 import org.opendaylight.yangtools.yang.common.QName;
25 import org.opendaylight.yangtools.yang.data.api.ModifyAction;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
31 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
32 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
40 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.AttributesBuilder;
41 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
42 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
43 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
44 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
45 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
46 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
47 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
48 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
49 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
50 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
51 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
52 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
53 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
54 import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema;
55
56 /**
57  * Base strategy for converting an instance identifier into a normalized node structure for container-like types.
58  */
59 abstract class InstanceIdToCompositeNodes<T extends PathArgument> extends InstanceIdToNodes<T> {
60
61     protected InstanceIdToCompositeNodes(final T identifier) {
62         super(identifier);
63     }
64
65     private static AugmentationIdentifier augmentationIdentifierFrom(final AugmentationSchemaNode augmentation) {
66         final ImmutableSet.Builder<QName> potentialChildren = ImmutableSet.builder();
67         for (final DataSchemaNode child : augmentation.getChildNodes()) {
68             potentialChildren.add(child.getQName());
69         }
70         return new AugmentationIdentifier(potentialChildren.build());
71     }
72
73     @Override
74     @SuppressWarnings("unchecked")
75     public final NormalizedNode<?, ?> create(final YangInstanceIdentifier instanceId,
76             final Optional<NormalizedNode<?, ?>> lastChild, final Optional<Entry<QName,ModifyAction>> operation) {
77         checkNotNull(instanceId);
78         final Iterator<PathArgument> iterator = instanceId.getPathArguments().iterator();
79         final PathArgument legacyData = iterator.next();
80
81         if (!isMixin() && getIdentifier().getNodeType() != null) {
82             checkArgument(getIdentifier().getNodeType().equals(legacyData.getNodeType()),
83                     "Node QName must be %s was %s", getIdentifier().getNodeType(), legacyData.getNodeType());
84         }
85         @SuppressWarnings("rawtypes")
86         final NormalizedNodeContainerBuilder builder = createBuilder(legacyData);
87
88         if (iterator.hasNext()) {
89             final PathArgument childPath = iterator.next();
90             final InstanceIdToNodes<?> childOp = getChildOperation(childPath);
91
92             final YangInstanceIdentifier childId = YangInstanceIdentifier.create(
93                 Iterables.skip(instanceId.getPathArguments(), 1));
94             builder.addChild(childOp.create(childId, lastChild, operation));
95         } else {
96             if (lastChild.isPresent()) {
97                 builder.withValue(ImmutableList.copyOf((Collection<?>) lastChild.get().getValue()));
98             }
99             if (operation.isPresent()) {
100                 checkArgument(builder instanceof AttributesBuilder<?>);
101                 addModifyOpIfPresent(operation, (AttributesBuilder<?>) builder);
102             }
103         }
104
105         return builder.build();
106     }
107
108     @SuppressWarnings("checkstyle:illegalCatch")
109     private InstanceIdToNodes<?> getChildOperation(final PathArgument childPath) {
110         final InstanceIdToNodes<?> childOp;
111         try {
112             childOp = getChild(childPath);
113         } catch (final RuntimeException e) {
114             throw new IllegalArgumentException(String.format("Failed to process child node %s", childPath), e);
115         }
116         checkArgument(childOp != null, "Node %s is not allowed inside %s", childPath, getIdentifier());
117         return childOp;
118     }
119
120     protected abstract NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(PathArgument compositeNode);
121
122     abstract static class DataContainerNormalizationOperation<T extends PathArgument>
123             extends InstanceIdToCompositeNodes<T> {
124
125         private final DataNodeContainer schema;
126         private final Map<PathArgument, InstanceIdToNodes<?>> byArg;
127
128         protected DataContainerNormalizationOperation(final T identifier, final DataNodeContainer schema) {
129             super(identifier);
130             this.schema = schema;
131             this.byArg = new ConcurrentHashMap<>();
132         }
133
134         @Override
135         public InstanceIdToNodes<?> getChild(final PathArgument child) {
136             InstanceIdToNodes<?> potential = byArg.get(child);
137             if (potential != null) {
138                 return potential;
139             }
140             potential = fromLocalSchema(child);
141             return register(potential);
142         }
143
144         private InstanceIdToNodes<?> fromLocalSchema(final PathArgument child) {
145             if (child instanceof AugmentationIdentifier) {
146                 return fromSchemaAndQNameChecked(schema, ((AugmentationIdentifier) child).getPossibleChildNames()
147                         .iterator().next());
148             }
149             return fromSchemaAndQNameChecked(schema, child.getNodeType());
150         }
151
152         private InstanceIdToNodes<?> register(final InstanceIdToNodes<?> potential) {
153             if (potential != null) {
154                 byArg.put(potential.getIdentifier(), potential);
155             }
156             return potential;
157         }
158     }
159
160     static final class ListItemNormalization extends DataContainerNormalizationOperation<NodeIdentifierWithPredicates> {
161         protected ListItemNormalization(final NodeIdentifierWithPredicates identifier, final ListSchemaNode schema) {
162             super(identifier, schema);
163         }
164
165         @Override
166         protected DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> createBuilder(
167                 final PathArgument currentArg) {
168             final NodeIdentifierWithPredicates arg = (NodeIdentifierWithPredicates) currentArg;
169             final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = Builders
170                     .mapEntryBuilder().withNodeIdentifier(arg);
171             for (final Entry<QName, Object> keyValue : arg.getKeyValues().entrySet()) {
172                 builder.addChild(Builders.leafBuilder()
173                         .withNodeIdentifier(NodeIdentifier.create(keyValue.getKey())).withValue(keyValue.getValue())
174                         .build());
175             }
176             return builder;
177         }
178
179         @Override
180         boolean isMixin() {
181             return false;
182         }
183     }
184
185     static final class UnkeyedListItemNormalization extends DataContainerNormalizationOperation<NodeIdentifier> {
186         UnkeyedListItemNormalization(final ListSchemaNode schema) {
187             super(NodeIdentifier.create(schema.getQName()), schema);
188         }
189
190         @Override
191         protected DataContainerNodeAttrBuilder<NodeIdentifier, UnkeyedListEntryNode> createBuilder(
192                 final PathArgument compositeNode) {
193             return Builders.unkeyedListEntryBuilder().withNodeIdentifier(getIdentifier());
194         }
195
196         @Override
197         boolean isMixin() {
198             return false;
199         }
200     }
201
202     static final class ContainerTransformation extends DataContainerNormalizationOperation<NodeIdentifier> {
203         ContainerTransformation(final ContainerSchemaNode schema) {
204             super(NodeIdentifier.create(schema.getQName()), schema);
205         }
206
207         @Override
208         protected DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> createBuilder(
209                 final PathArgument compositeNode) {
210             return Builders.containerBuilder().withNodeIdentifier(getIdentifier());
211         }
212
213         @Override
214         boolean isMixin() {
215             return false;
216         }
217     }
218
219     static final class OrderedLeafListMixinNormalization extends UnorderedLeafListMixinNormalization {
220         OrderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
221             super(potential);
222         }
223
224         @Override
225         protected ListNodeBuilder<?, ?> createBuilder(final PathArgument compositeNode) {
226             return Builders.orderedLeafSetBuilder().withNodeIdentifier(getIdentifier());
227         }
228     }
229
230     static class UnorderedLeafListMixinNormalization extends InstanceIdToCompositeNodes<NodeIdentifier> {
231         private final InstanceIdToNodes<?> innerOp;
232
233         UnorderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
234             super(NodeIdentifier.create(potential.getQName()));
235             innerOp = new InstanceIdToSimpleNodes.LeafListEntryNormalization(potential);
236         }
237
238         @Override
239         protected ListNodeBuilder<?, ?> createBuilder(final PathArgument compositeNode) {
240             return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier());
241         }
242
243         @Override
244         public InstanceIdToNodes<?> getChild(final PathArgument child) {
245             if (child instanceof NodeWithValue) {
246                 return innerOp;
247             }
248             return null;
249         }
250
251         @Override
252         boolean isMixin() {
253             return true;
254         }
255     }
256
257     static final class AugmentationNormalization extends DataContainerNormalizationOperation<AugmentationIdentifier> {
258         AugmentationNormalization(final AugmentationSchemaNode augmentation, final DataNodeContainer schema) {
259             super(augmentationIdentifierFrom(augmentation), EffectiveAugmentationSchema.create(augmentation, schema));
260         }
261
262         @Override
263         protected DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> createBuilder(
264                 final PathArgument compositeNode) {
265             return Builders.augmentationBuilder().withNodeIdentifier(getIdentifier());
266         }
267
268         @Override
269         boolean isMixin() {
270             return true;
271         }
272     }
273
274     static class UnorderedMapMixinNormalization extends InstanceIdToCompositeNodes<NodeIdentifier> {
275         private final ListItemNormalization innerNode;
276
277         UnorderedMapMixinNormalization(final ListSchemaNode list) {
278             super(NodeIdentifier.create(list.getQName()));
279             this.innerNode = new ListItemNormalization(new NodeIdentifierWithPredicates(list.getQName(),
280                     Collections.emptyMap()), list);
281         }
282
283         @Override
284         protected CollectionNodeBuilder<MapEntryNode, ? extends MapNode> createBuilder(
285                 final PathArgument compositeNode) {
286             return Builders.mapBuilder().withNodeIdentifier(getIdentifier());
287         }
288
289         @Override
290         public InstanceIdToNodes<?> getChild(final PathArgument child) {
291             if (child.getNodeType().equals(getIdentifier().getNodeType())) {
292                 return innerNode;
293             }
294             return null;
295         }
296
297         @Override
298         boolean isMixin() {
299             return true;
300         }
301     }
302
303     static final class OrderedMapMixinNormalization extends UnorderedMapMixinNormalization {
304         OrderedMapMixinNormalization(final ListSchemaNode list) {
305             super(list);
306         }
307
308         @Override
309         protected CollectionNodeBuilder<MapEntryNode, OrderedMapNode> createBuilder(final PathArgument compositeNode) {
310             return Builders.orderedMapBuilder().withNodeIdentifier(getIdentifier());
311         }
312
313     }
314
315     static final class ChoiceNodeNormalization extends InstanceIdToCompositeNodes<NodeIdentifier> {
316         private final ImmutableMap<PathArgument, InstanceIdToNodes<?>> byArg;
317
318         ChoiceNodeNormalization(final ChoiceSchemaNode schema) {
319             super(NodeIdentifier.create(schema.getQName()));
320             final ImmutableMap.Builder<PathArgument, InstanceIdToNodes<?>> byArgBuilder = ImmutableMap.builder();
321
322             for (final CaseSchemaNode caze : schema.getCases().values()) {
323                 for (final DataSchemaNode cazeChild : caze.getChildNodes()) {
324                     final InstanceIdToNodes<?> childOp = fromDataSchemaNode(cazeChild);
325                     byArgBuilder.put(childOp.getIdentifier(), childOp);
326                 }
327             }
328             byArg = byArgBuilder.build();
329         }
330
331         @Override
332         public InstanceIdToNodes<?> getChild(final PathArgument child) {
333             return byArg.get(child);
334         }
335
336         @Override
337         protected DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> createBuilder(final PathArgument compositeNode) {
338             return Builders.choiceBuilder().withNodeIdentifier(getIdentifier());
339         }
340
341         @Override
342         boolean isMixin() {
343             return true;
344         }
345     }
346 }