2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.data.impl.schema;
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;
14 import com.google.common.collect.ImmutableList;
15 import com.google.common.collect.ImmutableMap;
16 import com.google.common.collect.Iterables;
17 import com.google.common.collect.Maps;
18 import java.util.Collection;
19 import java.util.Iterator;
20 import java.util.LinkedHashMap;
21 import java.util.List;
23 import java.util.Map.Entry;
24 import java.util.Optional;
25 import java.util.concurrent.ConcurrentHashMap;
26 import org.eclipse.jdt.annotation.NonNull;
27 import org.opendaylight.yangtools.util.ImmutableOffsetMap;
28 import org.opendaylight.yangtools.yang.common.QName;
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.CollectionNodeBuilder;
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.data.util.DataSchemaContextNode;
47 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
48 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
49 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
50 import org.opendaylight.yangtools.yang.model.api.ContainerLike;
51 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
52 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
53 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
54 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
55 import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
60 * Base strategy for converting an instance identifier into a normalized node structure for container-like types.
62 abstract class InstanceIdToCompositeNodes<T extends PathArgument> extends InstanceIdToNodes<T> {
63 private static final Logger LOG = LoggerFactory.getLogger(InstanceIdToCompositeNodes.class);
65 InstanceIdToCompositeNodes(final T identifier) {
70 @SuppressWarnings("unchecked")
71 final NormalizedNode<?, ?> create(final PathArgument first, final Iterator<PathArgument> others,
72 final Optional<NormalizedNode<?, ?>> lastChild) {
74 final QName type = getIdentifier().getNodeType();
76 final QName firstType = first.getNodeType();
77 checkArgument(type.equals(firstType), "Node QName must be %s was %s", type, firstType);
81 @SuppressWarnings("rawtypes")
82 final NormalizedNodeContainerBuilder builder = createBuilder(first);
84 if (others.hasNext()) {
85 final PathArgument childPath = others.next();
86 final InstanceIdToNodes<?> childOp = getChildOperation(childPath);
87 builder.addChild(childOp.create(childPath, others, lastChild));
88 } else if (lastChild.isPresent()) {
89 builder.withValue(ImmutableList.copyOf((Collection<?>) lastChild.get().getValue()));
92 return builder.build();
95 @SuppressWarnings("checkstyle:illegalCatch")
96 private InstanceIdToNodes<?> getChildOperation(final PathArgument childPath) {
97 final InstanceIdToNodes<?> childOp;
99 childOp = getChild(childPath);
100 } catch (final RuntimeException e) {
101 throw new IllegalArgumentException(String.format("Failed to process child node %s", childPath), e);
103 checkArgument(childOp != null, "Node %s is not allowed inside %s", childPath, getIdentifier());
107 abstract NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(PathArgument compositeNode);
109 abstract static class DataContainerNormalizationOperation<T extends PathArgument, S extends DataNodeContainer>
110 extends InstanceIdToCompositeNodes<T> {
112 private final Map<PathArgument, InstanceIdToNodes<?>> byArg = new ConcurrentHashMap<>();
113 private final @NonNull S schema;
115 DataContainerNormalizationOperation(final T identifier, final S schema) {
117 this.schema = requireNonNull(schema);
121 final InstanceIdToNodes<?> getChild(final PathArgument child) {
122 final InstanceIdToNodes<?> existing = byArg.get(child);
123 if (existing != null) {
126 return register(fromLocalSchema(child));
129 final @NonNull S schema() {
133 private InstanceIdToNodes<?> fromLocalSchema(final PathArgument child) {
134 if (child instanceof AugmentationIdentifier) {
135 return fromSchemaAndQNameChecked(schema, ((AugmentationIdentifier) child).getPossibleChildNames()
138 return fromSchemaAndQNameChecked(schema, child.getNodeType());
141 private InstanceIdToNodes<?> register(final InstanceIdToNodes<?> potential) {
142 if (potential != null) {
143 byArg.put(potential.getIdentifier(), potential);
149 static final class MapEntryNormalization
150 extends DataContainerNormalizationOperation<NodeIdentifierWithPredicates, ListSchemaNode> {
151 MapEntryNormalization(final ListSchemaNode schema) {
152 super(NodeIdentifierWithPredicates.of(schema.getQName()), schema);
161 DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> createBuilder(
162 final PathArgument currentArg) {
163 final NodeIdentifierWithPredicates arg = (NodeIdentifierWithPredicates) currentArg;
164 return createBuilder(arg.size() < 2 ? arg : reorderPredicates(schema().getKeyDefinition(), arg));
167 private static DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> createBuilder(
168 final NodeIdentifierWithPredicates arg) {
169 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = Builders
170 .mapEntryBuilder().withNodeIdentifier(arg);
171 for (final Entry<QName, Object> keyValue : arg.entrySet()) {
172 builder.addChild(Builders.leafBuilder()
173 .withNodeIdentifier(NodeIdentifier.create(keyValue.getKey())).withValue(keyValue.getValue())
179 private static NodeIdentifierWithPredicates reorderPredicates(final List<QName> keys,
180 final NodeIdentifierWithPredicates arg) {
181 if (Iterables.elementsEqual(keys, arg.keySet())) {
182 // Iteration order matches key order, reuse the identifier
186 // We care about iteration order here!
187 final LinkedHashMap<QName, Object> map = Maps.newLinkedHashMapWithExpectedSize(arg.size());
188 for (QName qname : keys) {
189 final Object value = arg.getValue(qname);
191 map.put(qname, value);
194 if (map.size() < arg.size()) {
195 // Okay, this should not happen, but let's handle that anyway
196 LOG.debug("Extra predicates in {} while expecting {}", arg, keys);
197 for (Entry<QName, Object> entry : arg.entrySet()) {
198 map.putIfAbsent(entry.getKey(), entry.getValue());
202 // This copy retains iteration order and since we have more than one argument, it should always be
203 // and ImmutableOffsetMap -- which is guaranteed to be taken as-is
204 final Map<QName, Object> copy = ImmutableOffsetMap.orderedCopyOf(map);
205 verify(copy instanceof ImmutableOffsetMap);
206 return NodeIdentifierWithPredicates.of(arg.getNodeType(), (ImmutableOffsetMap<QName, Object>) copy);
210 static final class UnkeyedListItemNormalization
211 extends DataContainerNormalizationOperation<NodeIdentifier, ListSchemaNode> {
212 UnkeyedListItemNormalization(final ListSchemaNode schema) {
213 super(NodeIdentifier.create(schema.getQName()), schema);
217 DataContainerNodeBuilder<NodeIdentifier, UnkeyedListEntryNode> createBuilder(
218 final PathArgument compositeNode) {
219 return Builders.unkeyedListEntryBuilder().withNodeIdentifier(getIdentifier());
228 static final class ContainerTransformation
229 extends DataContainerNormalizationOperation<NodeIdentifier, ContainerLike> {
230 ContainerTransformation(final ContainerLike schema) {
231 super(NodeIdentifier.create(schema.getQName()), schema);
235 DataContainerNodeBuilder<NodeIdentifier, ContainerNode> createBuilder(final PathArgument compositeNode) {
236 return Builders.containerBuilder().withNodeIdentifier(getIdentifier());
245 static final class OrderedLeafListMixinNormalization extends UnorderedLeafListMixinNormalization {
246 OrderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
251 ListNodeBuilder<?, ?> createBuilder(final PathArgument compositeNode) {
252 return Builders.orderedLeafSetBuilder().withNodeIdentifier(getIdentifier());
256 static class UnorderedLeafListMixinNormalization extends InstanceIdToCompositeNodes<NodeIdentifier> {
257 private final InstanceIdToNodes<?> innerOp;
259 UnorderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
260 super(NodeIdentifier.create(potential.getQName()));
261 innerOp = new InstanceIdToSimpleNodes.LeafListEntryNormalization(potential);
265 ListNodeBuilder<?, ?> createBuilder(final PathArgument compositeNode) {
266 return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier());
270 final InstanceIdToNodes<?> getChild(final PathArgument child) {
271 return child instanceof NodeWithValue ? innerOp : null;
275 final boolean isMixin() {
280 static final class AugmentationNormalization
281 extends DataContainerNormalizationOperation<AugmentationIdentifier, AugmentationSchemaNode> {
282 AugmentationNormalization(final AugmentationSchemaNode augmentation, final DataNodeContainer schema) {
283 super(DataSchemaContextNode.augmentationIdentifierFrom(augmentation),
284 EffectiveAugmentationSchema.create(augmentation, schema));
288 DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> createBuilder(
289 final PathArgument compositeNode) {
290 return Builders.augmentationBuilder().withNodeIdentifier(getIdentifier());
299 static class UnorderedMapMixinNormalization extends InstanceIdToCompositeNodes<NodeIdentifier> {
300 private final MapEntryNormalization innerNode;
302 UnorderedMapMixinNormalization(final ListSchemaNode list) {
303 super(NodeIdentifier.create(list.getQName()));
304 this.innerNode = new MapEntryNormalization(list);
308 CollectionNodeBuilder<MapEntryNode, ? extends MapNode> createBuilder(final PathArgument compositeNode) {
309 return Builders.mapBuilder().withNodeIdentifier(getIdentifier());
313 final InstanceIdToNodes<?> getChild(final PathArgument child) {
314 return child.getNodeType().equals(getIdentifier().getNodeType()) ? innerNode : null;
318 final boolean isMixin() {
323 static final class OrderedMapMixinNormalization extends UnorderedMapMixinNormalization {
324 OrderedMapMixinNormalization(final ListSchemaNode list) {
329 CollectionNodeBuilder<MapEntryNode, OrderedMapNode> createBuilder(final PathArgument compositeNode) {
330 return Builders.orderedMapBuilder().withNodeIdentifier(getIdentifier());
334 static final class ChoiceNodeNormalization extends InstanceIdToCompositeNodes<NodeIdentifier> {
335 private final ImmutableMap<PathArgument, InstanceIdToNodes<?>> byArg;
337 ChoiceNodeNormalization(final ChoiceSchemaNode schema) {
338 super(NodeIdentifier.create(schema.getQName()));
339 final ImmutableMap.Builder<PathArgument, InstanceIdToNodes<?>> byArgBuilder = ImmutableMap.builder();
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);
347 byArg = byArgBuilder.build();
351 InstanceIdToNodes<?> getChild(final PathArgument child) {
352 return byArg.get(child);
356 DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> createBuilder(final PathArgument compositeNode) {
357 return Builders.choiceBuilder().withNodeIdentifier(getIdentifier());