2 * Copyright (c) 2014 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.netconf.sal.restconf.impl;
10 import static com.google.common.base.Verify.verifyNotNull;
12 import com.google.common.collect.ImmutableMap;
13 import com.google.common.collect.ImmutableSet;
14 import com.google.common.collect.Iterables;
17 import java.util.concurrent.ConcurrentHashMap;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.opendaylight.yangtools.concepts.Identifiable;
20 import org.opendaylight.yangtools.yang.common.Empty;
21 import org.opendaylight.yangtools.yang.common.QName;
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.util.DataSchemaContextNode;
28 import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
31 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.ContainerLike;
34 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
36 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
38 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
39 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
41 import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema;
42 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
44 abstract class DataNormalizationOperation<T extends PathArgument> implements Identifiable<T> {
45 private final T identifier;
47 DataNormalizationOperation(final T identifier) {
48 this.identifier = identifier;
51 static DataNormalizationOperation<?> from(final EffectiveModelContext ctx) {
52 return new ContainerNormalization(ctx);
56 public T getIdentifier() {
64 Set<QName> getQNameIdentifiers() {
65 return ImmutableSet.of(identifier.getNodeType());
68 abstract DataNormalizationOperation<?> getChild(PathArgument child) throws DataNormalizationException;
70 abstract DataNormalizationOperation<?> getChild(QName child) throws DataNormalizationException;
72 abstract DataNormalizationOperation<?> enterChild(QName child, SchemaInferenceStack stack)
73 throws DataNormalizationException;
75 abstract DataNormalizationOperation<?> enterChild(PathArgument child, SchemaInferenceStack stack)
76 throws DataNormalizationException;
78 void pushToStack(final SchemaInferenceStack stack) {
79 // Accurate for most subclasses
80 stack.enterSchemaTree(getIdentifier().getNodeType());
83 private abstract static class SimpleTypeNormalization<T extends PathArgument>
84 extends DataNormalizationOperation<T> {
85 SimpleTypeNormalization(final T identifier) {
90 final DataNormalizationOperation<?> getChild(final PathArgument child) {
95 final DataNormalizationOperation<?> getChild(final QName child) {
100 final DataNormalizationOperation<?> enterChild(final QName child, final SchemaInferenceStack stack) {
105 final DataNormalizationOperation<?> enterChild(final PathArgument child, final SchemaInferenceStack stack) {
110 private static final class LeafNormalization extends SimpleTypeNormalization<NodeIdentifier> {
111 LeafNormalization(final LeafSchemaNode potential) {
112 super(new NodeIdentifier(potential.getQName()));
116 private static final class LeafListEntryNormalization extends SimpleTypeNormalization<NodeWithValue> {
117 LeafListEntryNormalization(final LeafListSchemaNode potential) {
118 super(new NodeWithValue<>(potential.getQName(), Empty.value()));
122 protected void pushToStack(final SchemaInferenceStack stack) {
127 private abstract static class DataContainerNormalizationOperation<T extends PathArgument>
128 extends DataNormalizationOperation<T> {
129 private final DataNodeContainer schema;
130 private final Map<QName, DataNormalizationOperation<?>> byQName = new ConcurrentHashMap<>();
131 private final Map<PathArgument, DataNormalizationOperation<?>> byArg = new ConcurrentHashMap<>();
133 DataContainerNormalizationOperation(final T identifier, final DataNodeContainer schema) {
135 this.schema = schema;
139 DataNormalizationOperation<?> getChild(final PathArgument child) throws DataNormalizationException {
140 DataNormalizationOperation<?> potential = byArg.get(child);
141 if (potential != null) {
144 potential = fromLocalSchema(child);
145 return register(potential);
149 DataNormalizationOperation<?> getChild(final QName child) throws DataNormalizationException {
150 DataNormalizationOperation<?> potential = byQName.get(child);
151 if (potential != null) {
154 potential = fromLocalSchemaAndQName(schema, child);
155 return register(potential);
159 final DataNormalizationOperation<?> enterChild(final QName child, final SchemaInferenceStack stack)
160 throws DataNormalizationException {
161 return pushToStack(getChild(child), stack);
165 final DataNormalizationOperation<?> enterChild(final PathArgument child, final SchemaInferenceStack stack)
166 throws DataNormalizationException {
167 return pushToStack(getChild(child), stack);
170 private static DataNormalizationOperation<?> pushToStack(final DataNormalizationOperation<?> child,
171 final SchemaInferenceStack stack) {
173 child.pushToStack(stack);
178 private DataNormalizationOperation<?> fromLocalSchema(final PathArgument child)
179 throws DataNormalizationException {
180 if (child instanceof AugmentationIdentifier) {
181 return fromSchemaAndQNameChecked(schema, ((AugmentationIdentifier) child).getPossibleChildNames()
184 return fromSchemaAndQNameChecked(schema, child.getNodeType());
187 DataNormalizationOperation<?> fromLocalSchemaAndQName(final DataNodeContainer schema2,
188 final QName child) throws DataNormalizationException {
189 return fromSchemaAndQNameChecked(schema2, child);
192 private DataNormalizationOperation<?> register(final DataNormalizationOperation<?> potential) {
193 if (potential != null) {
194 byArg.put(potential.getIdentifier(), potential);
195 for (final QName qname : potential.getQNameIdentifiers()) {
196 byQName.put(qname, potential);
202 private static DataNormalizationOperation<?> fromSchemaAndQNameChecked(final DataNodeContainer schema,
203 final QName child) throws DataNormalizationException {
205 final DataSchemaNode result = findChildSchemaNode(schema, child);
206 if (result == null) {
207 throw new DataNormalizationException(String.format(
208 "Supplied QName %s is not valid according to schema %s, potential children nodes: %s", child,
209 schema,schema.getChildNodes()));
212 // We try to look up if this node was added by augmentation
213 if (schema instanceof DataSchemaNode && result.isAugmenting()) {
214 return fromAugmentation(schema, (AugmentationTarget) schema, result);
216 return fromDataSchemaNode(result);
220 private static final class ListItemNormalization extends
221 DataContainerNormalizationOperation<NodeIdentifierWithPredicates> {
222 ListItemNormalization(final NodeIdentifierWithPredicates identifier, final ListSchemaNode schema) {
223 super(identifier, schema);
227 protected void pushToStack(final SchemaInferenceStack stack) {
232 private static final class UnkeyedListItemNormalization
233 extends DataContainerNormalizationOperation<NodeIdentifier> {
234 UnkeyedListItemNormalization(final ListSchemaNode schema) {
235 super(new NodeIdentifier(schema.getQName()), schema);
239 protected void pushToStack(final SchemaInferenceStack stack) {
244 private static final class ContainerNormalization extends DataContainerNormalizationOperation<NodeIdentifier> {
245 ContainerNormalization(final ContainerLike schema) {
246 super(new NodeIdentifier(schema.getQName()), schema);
250 private abstract static class MixinNormalizationOp<T extends PathArgument> extends DataNormalizationOperation<T> {
251 MixinNormalizationOp(final T identifier) {
256 final boolean isMixin() {
261 private abstract static class ListLikeNormalizationOp<T extends PathArgument> extends MixinNormalizationOp<T> {
262 ListLikeNormalizationOp(final T identifier) {
267 protected final DataNormalizationOperation<?> enterChild(final QName child, final SchemaInferenceStack stack)
268 throws DataNormalizationException {
269 // Stack is already pointing to the corresponding statement, now we are just working with the child
270 return getChild(child);
274 protected final DataNormalizationOperation<?> enterChild(final PathArgument child,
275 final SchemaInferenceStack stack) throws DataNormalizationException {
276 return getChild(child);
280 private static final class LeafListMixinNormalization extends ListLikeNormalizationOp<NodeIdentifier> {
281 private final DataNormalizationOperation<?> innerOp;
283 LeafListMixinNormalization(final LeafListSchemaNode potential) {
284 super(new NodeIdentifier(potential.getQName()));
285 innerOp = new LeafListEntryNormalization(potential);
289 DataNormalizationOperation<?> getChild(final PathArgument child) {
290 if (child instanceof NodeWithValue) {
297 DataNormalizationOperation<?> getChild(final QName child) {
298 if (getIdentifier().getNodeType().equals(child)) {
305 private static final class AugmentationNormalization
306 extends DataContainerNormalizationOperation<AugmentationIdentifier> {
308 AugmentationNormalization(final AugmentationSchemaNode augmentation, final DataNodeContainer schema) {
309 super(DataSchemaContextNode.augmentationIdentifierFrom(augmentation),
310 new EffectiveAugmentationSchema(augmentation, schema));
319 DataNormalizationOperation<?> fromLocalSchemaAndQName(final DataNodeContainer schema, final QName child) {
320 final DataSchemaNode result = findChildSchemaNode(schema, child);
321 if (result == null) {
325 // We try to look up if this node was added by augmentation
326 if (schema instanceof DataSchemaNode && result.isAugmenting()) {
327 return fromAugmentation(schema, (AugmentationTarget) schema, result);
329 return fromDataSchemaNode(result);
333 Set<QName> getQNameIdentifiers() {
334 return getIdentifier().getPossibleChildNames();
338 void pushToStack(final SchemaInferenceStack stack) {
343 private static final class MapMixinNormalization extends ListLikeNormalizationOp<NodeIdentifier> {
344 private final ListItemNormalization innerNode;
346 MapMixinNormalization(final ListSchemaNode list) {
347 super(new NodeIdentifier(list.getQName()));
348 innerNode = new ListItemNormalization(NodeIdentifierWithPredicates.of(list.getQName()), list);
352 DataNormalizationOperation<?> getChild(final PathArgument child) {
353 if (child.getNodeType().equals(getIdentifier().getNodeType())) {
360 DataNormalizationOperation<?> getChild(final QName child) {
361 if (getIdentifier().getNodeType().equals(child)) {
368 private static final class UnkeyedListMixinNormalization extends ListLikeNormalizationOp<NodeIdentifier> {
369 private final UnkeyedListItemNormalization innerNode;
371 UnkeyedListMixinNormalization(final ListSchemaNode list) {
372 super(new NodeIdentifier(list.getQName()));
373 innerNode = new UnkeyedListItemNormalization(list);
377 DataNormalizationOperation<?> getChild(final PathArgument child) {
378 if (child.getNodeType().equals(getIdentifier().getNodeType())) {
385 DataNormalizationOperation<?> getChild(final QName child) {
386 if (getIdentifier().getNodeType().equals(child)) {
393 private static final class ChoiceNodeNormalization extends MixinNormalizationOp<NodeIdentifier> {
394 private final ImmutableMap<QName, DataNormalizationOperation<?>> byQName;
395 private final ImmutableMap<PathArgument, DataNormalizationOperation<?>> byArg;
396 private final ImmutableMap<DataNormalizationOperation<?>, QName> childToCase;
398 ChoiceNodeNormalization(final ChoiceSchemaNode schema) {
399 super(new NodeIdentifier(schema.getQName()));
400 ImmutableMap.Builder<DataNormalizationOperation<?>, QName> childToCaseBuilder = ImmutableMap.builder();
401 final ImmutableMap.Builder<QName, DataNormalizationOperation<?>> byQNameBuilder = ImmutableMap.builder();
402 final ImmutableMap.Builder<PathArgument, DataNormalizationOperation<?>> byArgBuilder =
403 ImmutableMap.builder();
405 for (final CaseSchemaNode caze : schema.getCases()) {
406 for (final DataSchemaNode cazeChild : caze.getChildNodes()) {
407 final DataNormalizationOperation<?> childOp = fromDataSchemaNode(cazeChild);
408 byArgBuilder.put(childOp.getIdentifier(), childOp);
409 childToCaseBuilder.put(childOp, caze.getQName());
410 for (final QName qname : childOp.getQNameIdentifiers()) {
411 byQNameBuilder.put(qname, childOp);
415 childToCase = childToCaseBuilder.build();
416 byQName = byQNameBuilder.build();
417 byArg = byArgBuilder.build();
421 DataNormalizationOperation<?> getChild(final PathArgument child) {
422 return byArg.get(child);
426 DataNormalizationOperation<?> getChild(final QName child) {
427 return byQName.get(child);
431 Set<QName> getQNameIdentifiers() {
432 return byQName.keySet();
436 DataNormalizationOperation<?> enterChild(final QName child, final SchemaInferenceStack stack) {
437 return pushToStack(getChild(child), stack);
441 DataNormalizationOperation<?> enterChild(final PathArgument child, final SchemaInferenceStack stack) {
442 return pushToStack(getChild(child), stack);
446 void pushToStack(final SchemaInferenceStack stack) {
447 stack.enterChoice(getIdentifier().getNodeType());
450 private DataNormalizationOperation<?> pushToStack(final DataNormalizationOperation<?> child,
451 final SchemaInferenceStack stack) {
453 final var caseName = verifyNotNull(childToCase.get(child), "No case statement for %s in %s", child,
455 stack.enterSchemaTree(caseName);
456 child.pushToStack(stack);
462 private static final class AnyxmlNormalization extends SimpleTypeNormalization<NodeIdentifier> {
463 AnyxmlNormalization(final AnyxmlSchemaNode schema) {
464 super(new NodeIdentifier(schema.getQName()));
468 private static @Nullable DataSchemaNode findChildSchemaNode(final DataNodeContainer parent, final QName child) {
469 final DataSchemaNode potential = parent.dataChildByName(child);
470 return potential != null ? potential : findChoice(parent, child);
473 private static @Nullable ChoiceSchemaNode findChoice(final DataNodeContainer parent, final QName child) {
474 for (final ChoiceSchemaNode choice : Iterables.filter(parent.getChildNodes(), ChoiceSchemaNode.class)) {
475 for (final CaseSchemaNode caze : choice.getCases()) {
476 if (findChildSchemaNode(caze, child) != null) {
485 * Returns a DataNormalizationOperation for provided child node.
488 * If supplied child is added by Augmentation this operation returns
489 * a DataNormalizationOperation for augmentation,
490 * otherwise returns a DataNormalizationOperation for child as
491 * call for {@link #fromDataSchemaNode(DataSchemaNode)}.
493 private static DataNormalizationOperation<?> fromAugmentation(final DataNodeContainer parent,
494 final AugmentationTarget parentAug, final DataSchemaNode child) {
495 for (final AugmentationSchemaNode aug : parentAug.getAvailableAugmentations()) {
496 if (aug.dataChildByName(child.getQName()) != null) {
497 return new AugmentationNormalization(aug, parent);
500 return fromDataSchemaNode(child);
503 static DataNormalizationOperation<?> fromDataSchemaNode(final DataSchemaNode potential) {
504 if (potential instanceof ContainerSchemaNode) {
505 return new ContainerNormalization((ContainerSchemaNode) potential);
506 } else if (potential instanceof ListSchemaNode) {
507 return fromListSchemaNode((ListSchemaNode) potential);
508 } else if (potential instanceof LeafSchemaNode) {
509 return new LeafNormalization((LeafSchemaNode) potential);
510 } else if (potential instanceof ChoiceSchemaNode) {
511 return new ChoiceNodeNormalization((ChoiceSchemaNode) potential);
512 } else if (potential instanceof LeafListSchemaNode) {
513 return new LeafListMixinNormalization((LeafListSchemaNode) potential);
514 } else if (potential instanceof AnyxmlSchemaNode) {
515 return new AnyxmlNormalization((AnyxmlSchemaNode) potential);
520 private static DataNormalizationOperation<?> fromListSchemaNode(final ListSchemaNode potential) {
521 if (potential.getKeyDefinition().isEmpty()) {
522 return new UnkeyedListMixinNormalization(potential);
524 return new MapMixinNormalization(potential);