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