Remove EffectiveAugmentationSchema.create()
[yangtools.git] / data / 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.Verify.verify;
12 import static java.util.Objects.requireNonNull;
13
14 import com.google.common.collect.ImmutableMap;
15 import com.google.common.collect.Iterables;
16 import com.google.common.collect.Maps;
17 import java.util.Iterator;
18 import java.util.LinkedHashMap;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Map.Entry;
22 import java.util.concurrent.ConcurrentHashMap;
23 import org.eclipse.jdt.annotation.NonNull;
24 import org.opendaylight.yangtools.util.ImmutableOffsetMap;
25 import org.opendaylight.yangtools.yang.common.QName;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
31 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.UserMapNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.builder.CollectionNodeBuilder;
40 import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder;
41 import org.opendaylight.yangtools.yang.data.api.schema.builder.ListNodeBuilder;
42 import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeContainerBuilder;
43 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
44 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
45 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
46 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
47 import org.opendaylight.yangtools.yang.model.api.ContainerLike;
48 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
49 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
50 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
51 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
52 import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
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     private static final Logger LOG = LoggerFactory.getLogger(InstanceIdToCompositeNodes.class);
61
62     InstanceIdToCompositeNodes(final T identifier) {
63         super(identifier);
64     }
65
66     @Override
67     @SuppressWarnings("unchecked")
68     final NormalizedNode create(final PathArgument first, final Iterator<PathArgument> others) {
69         if (!isMixin()) {
70             final QName type = getIdentifier().getNodeType();
71             if (type != null) {
72                 final QName firstType = first.getNodeType();
73                 checkArgument(type.equals(firstType), "Node QName must be %s was %s", type, firstType);
74             }
75         }
76
77         @SuppressWarnings("rawtypes")
78         final NormalizedNodeContainerBuilder builder = createBuilder(first);
79
80         if (others.hasNext()) {
81             final PathArgument childPath = others.next();
82             final InstanceIdToNodes<?> childOp = getChildOperation(childPath);
83             builder.addChild(childOp.create(childPath, others));
84         }
85
86         return builder.build();
87     }
88
89     @SuppressWarnings("checkstyle:illegalCatch")
90     private InstanceIdToNodes<?> getChildOperation(final PathArgument childPath) {
91         final InstanceIdToNodes<?> childOp;
92         try {
93             childOp = getChild(childPath);
94         } catch (final RuntimeException e) {
95             throw new IllegalArgumentException(String.format("Failed to process child node %s", childPath), e);
96         }
97         checkArgument(childOp != null, "Node %s is not allowed inside %s", childPath, getIdentifier());
98         return childOp;
99     }
100
101     abstract NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(PathArgument compositeNode);
102
103     abstract static class DataContainerNormalizationOperation<T extends PathArgument, S extends DataNodeContainer>
104             extends InstanceIdToCompositeNodes<T> {
105
106         private final Map<PathArgument, InstanceIdToNodes<?>> byArg = new ConcurrentHashMap<>();
107         private final @NonNull S schema;
108
109         DataContainerNormalizationOperation(final T identifier, final S schema) {
110             super(identifier);
111             this.schema = requireNonNull(schema);
112         }
113
114         @Override
115         final InstanceIdToNodes<?> getChild(final PathArgument child) {
116             final InstanceIdToNodes<?> existing = byArg.get(child);
117             if (existing != null) {
118                 return existing;
119             }
120             return register(fromLocalSchema(child));
121         }
122
123         final @NonNull S schema() {
124             return schema;
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 MapEntryNormalization
144             extends DataContainerNormalizationOperation<NodeIdentifierWithPredicates, ListSchemaNode> {
145         MapEntryNormalization(final ListSchemaNode schema) {
146             super(NodeIdentifierWithPredicates.of(schema.getQName()), schema);
147         }
148
149         @Override
150         boolean isMixin() {
151             return false;
152         }
153
154         @Override
155         DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> createBuilder(
156                 final PathArgument currentArg) {
157             final NodeIdentifierWithPredicates arg = (NodeIdentifierWithPredicates) currentArg;
158             return createBuilder(arg.size() < 2 ? arg : reorderPredicates(schema().getKeyDefinition(), arg));
159         }
160
161         private static DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> createBuilder(
162                 final NodeIdentifierWithPredicates arg) {
163             final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = Builders
164                     .mapEntryBuilder().withNodeIdentifier(arg);
165             for (final Entry<QName, Object> keyValue : arg.entrySet()) {
166                 builder.addChild(Builders.leafBuilder()
167                         .withNodeIdentifier(NodeIdentifier.create(keyValue.getKey())).withValue(keyValue.getValue())
168                         .build());
169             }
170             return builder;
171         }
172
173         private static NodeIdentifierWithPredicates reorderPredicates(final List<QName> keys,
174                 final NodeIdentifierWithPredicates arg) {
175             if (Iterables.elementsEqual(keys, arg.keySet())) {
176                 // Iteration order matches key order, reuse the identifier
177                 return arg;
178             }
179
180             // We care about iteration order here!
181             final LinkedHashMap<QName, Object> map = Maps.newLinkedHashMapWithExpectedSize(arg.size());
182             for (QName qname : keys) {
183                 final Object value = arg.getValue(qname);
184                 if (value != null) {
185                     map.put(qname, value);
186                 }
187             }
188             if (map.size() < arg.size()) {
189                 // Okay, this should not happen, but let's handle that anyway
190                 LOG.debug("Extra predicates in {} while expecting {}", arg, keys);
191                 for (Entry<QName, Object> entry : arg.entrySet()) {
192                     map.putIfAbsent(entry.getKey(), entry.getValue());
193                 }
194             }
195
196             // This copy retains iteration order and since we have more than one argument, it should always be
197             // and ImmutableOffsetMap -- which is guaranteed to be taken as-is
198             final Map<QName, Object> copy = ImmutableOffsetMap.orderedCopyOf(map);
199             verify(copy instanceof ImmutableOffsetMap);
200             return NodeIdentifierWithPredicates.of(arg.getNodeType(), (ImmutableOffsetMap<QName, Object>) copy);
201         }
202     }
203
204     static final class UnkeyedListItemNormalization
205             extends DataContainerNormalizationOperation<NodeIdentifier, ListSchemaNode> {
206         UnkeyedListItemNormalization(final ListSchemaNode schema) {
207             super(NodeIdentifier.create(schema.getQName()), schema);
208         }
209
210         @Override
211         DataContainerNodeBuilder<NodeIdentifier, UnkeyedListEntryNode> createBuilder(
212                 final PathArgument compositeNode) {
213             return Builders.unkeyedListEntryBuilder().withNodeIdentifier(getIdentifier());
214         }
215
216         @Override
217         boolean isMixin() {
218             return false;
219         }
220     }
221
222     static final class ContainerTransformation
223             extends DataContainerNormalizationOperation<NodeIdentifier, ContainerLike> {
224         ContainerTransformation(final ContainerLike schema) {
225             super(NodeIdentifier.create(schema.getQName()), schema);
226         }
227
228         @Override
229         DataContainerNodeBuilder<NodeIdentifier, ContainerNode> createBuilder(final PathArgument compositeNode) {
230             return Builders.containerBuilder().withNodeIdentifier(getIdentifier());
231         }
232
233         @Override
234         boolean isMixin() {
235             return false;
236         }
237     }
238
239     private abstract static class LeafListMixinNormalization extends InstanceIdToCompositeNodes<NodeIdentifier> {
240         private final InstanceIdToNodes<?> innerOp;
241
242         LeafListMixinNormalization(final LeafListSchemaNode potential) {
243             super(NodeIdentifier.create(potential.getQName()));
244             innerOp = new InstanceIdToSimpleNodes.LeafListEntryNormalization(potential);
245         }
246
247         @Override
248         final InstanceIdToNodes<?> getChild(final PathArgument child) {
249             return child instanceof NodeWithValue ? innerOp : null;
250         }
251
252         @Override
253         final boolean isMixin() {
254             return true;
255         }
256     }
257
258     static final class OrderedLeafListMixinNormalization extends LeafListMixinNormalization {
259         OrderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
260             super(potential);
261         }
262
263         @Override
264         ListNodeBuilder<?, ?> createBuilder(final PathArgument compositeNode) {
265             return Builders.orderedLeafSetBuilder().withNodeIdentifier(getIdentifier());
266         }
267     }
268
269     static class UnorderedLeafListMixinNormalization extends LeafListMixinNormalization {
270         UnorderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
271             super(potential);
272         }
273
274         @Override
275         ListNodeBuilder<?, ?> createBuilder(final PathArgument compositeNode) {
276             return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier());
277         }
278     }
279
280     static final class AugmentationNormalization
281             extends DataContainerNormalizationOperation<AugmentationIdentifier, AugmentationSchemaNode> {
282         AugmentationNormalization(final AugmentationSchemaNode augmentation, final DataNodeContainer schema) {
283             super(DataSchemaContextNode.augmentationIdentifierFrom(augmentation),
284                 new EffectiveAugmentationSchema(augmentation, schema));
285         }
286
287         @Override
288         DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> createBuilder(
289                 final PathArgument compositeNode) {
290             return Builders.augmentationBuilder().withNodeIdentifier(getIdentifier());
291         }
292
293         @Override
294         boolean isMixin() {
295             return true;
296         }
297     }
298
299     static class UnorderedMapMixinNormalization extends InstanceIdToCompositeNodes<NodeIdentifier> {
300         private final MapEntryNormalization innerNode;
301
302         UnorderedMapMixinNormalization(final ListSchemaNode list) {
303             super(NodeIdentifier.create(list.getQName()));
304             innerNode = new MapEntryNormalization(list);
305         }
306
307         @Override
308         CollectionNodeBuilder<MapEntryNode, ? extends MapNode> createBuilder(final PathArgument compositeNode) {
309             return Builders.mapBuilder().withNodeIdentifier(getIdentifier());
310         }
311
312         @Override
313         final InstanceIdToNodes<?> getChild(final PathArgument child) {
314             return child.getNodeType().equals(getIdentifier().getNodeType()) ? innerNode : null;
315         }
316
317         @Override
318         final boolean isMixin() {
319             return true;
320         }
321     }
322
323     static final class OrderedMapMixinNormalization extends UnorderedMapMixinNormalization {
324         OrderedMapMixinNormalization(final ListSchemaNode list) {
325             super(list);
326         }
327
328         @Override
329         CollectionNodeBuilder<MapEntryNode, UserMapNode> createBuilder(final PathArgument compositeNode) {
330             return Builders.orderedMapBuilder().withNodeIdentifier(getIdentifier());
331         }
332     }
333
334     static final class ChoiceNodeNormalization extends InstanceIdToCompositeNodes<NodeIdentifier> {
335         private final ImmutableMap<PathArgument, InstanceIdToNodes<?>> byArg;
336
337         ChoiceNodeNormalization(final ChoiceSchemaNode schema) {
338             super(NodeIdentifier.create(schema.getQName()));
339             final ImmutableMap.Builder<PathArgument, InstanceIdToNodes<?>> byArgBuilder = ImmutableMap.builder();
340
341             for (final CaseSchemaNode caze : schema.getCases()) {
342                 for (final DataSchemaNode cazeChild : caze.getChildNodes()) {
343                     final InstanceIdToNodes<?> childOp = fromDataSchemaNode(cazeChild);
344                     byArgBuilder.put(childOp.getIdentifier(), childOp);
345                 }
346             }
347             byArg = byArgBuilder.build();
348         }
349
350         @Override
351         InstanceIdToNodes<?> getChild(final PathArgument child) {
352             return byArg.get(child);
353         }
354
355         @Override
356         DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> createBuilder(final PathArgument compositeNode) {
357             return Builders.choiceBuilder().withNodeIdentifier(getIdentifier());
358         }
359
360         @Override
361         boolean isMixin() {
362             return true;
363         }
364     }
365 }