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