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