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 com.google.common.base.Optional;
11 import com.google.common.collect.ImmutableMap;
12 import com.google.common.collect.ImmutableSet;
13 import com.google.common.collect.Iterables;
14 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
15 import java.util.Collections;
16 import java.util.HashSet;
17 import java.util.List;
19 import java.util.Map.Entry;
21 import java.util.concurrent.ConcurrentHashMap;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.gaul.modernizer_maven_annotations.SuppressModernizer;
24 import org.opendaylight.yangtools.concepts.Identifiable;
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.MapEntryNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
33 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
34 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
35 import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
38 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
39 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
42 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
43 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
45 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
46 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
47 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
48 import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema;
50 abstract class DataNormalizationOperation<T extends PathArgument> implements Identifiable<T> {
51 private final T identifier;
52 private final DataSchemaNode dataSchemaNode;
55 public T getIdentifier() {
59 protected DataNormalizationOperation(final T identifier, final SchemaNode schema) {
60 this.identifier = identifier;
61 dataSchemaNode = schema instanceof DataSchemaNode ? (DataSchemaNode)schema : null;
64 public boolean isMixin() {
69 public boolean isKeyedEntry() {
73 protected Set<QName> getQNameIdentifiers() {
74 return Collections.singleton(identifier.getNodeType());
77 public abstract DataNormalizationOperation<?> getChild(PathArgument child) throws DataNormalizationException;
79 public abstract DataNormalizationOperation<?> getChild(QName child) throws DataNormalizationException;
81 public abstract boolean isLeaf();
84 public Optional<DataSchemaNode> getDataSchemaNode() {
85 return Optional.fromNullable(dataSchemaNode);
88 private abstract static class SimpleTypeNormalization<T extends PathArgument>
89 extends DataNormalizationOperation<T> {
90 SimpleTypeNormalization(final T identifier, final DataSchemaNode potential) {
91 super(identifier,potential);
95 public DataNormalizationOperation<?> getChild(final PathArgument child) {
100 public DataNormalizationOperation<?> getChild(final QName child) {
105 public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
110 public boolean isLeaf() {
115 private static final class LeafNormalization extends SimpleTypeNormalization<NodeIdentifier> {
116 LeafNormalization(final LeafSchemaNode potential) {
117 super(new NodeIdentifier(potential.getQName()),potential);
121 private static final class LeafListEntryNormalization extends SimpleTypeNormalization<NodeWithValue> {
122 LeafListEntryNormalization(final LeafListSchemaNode potential) {
123 super(new NodeWithValue(potential.getQName(), null),potential);
127 public boolean isKeyedEntry() {
132 private abstract static class CompositeNodeNormalizationOperation<T extends PathArgument>
133 extends DataNormalizationOperation<T> {
134 CompositeNodeNormalizationOperation(final T identifier, final DataSchemaNode schema) {
135 super(identifier,schema);
139 public boolean isLeaf() {
144 private abstract static class DataContainerNormalizationOperation<T extends PathArgument>
145 extends CompositeNodeNormalizationOperation<T> {
146 private final DataNodeContainer schema;
147 private final Map<QName, DataNormalizationOperation<?>> byQName;
148 private final Map<PathArgument, DataNormalizationOperation<?>> byArg;
150 DataContainerNormalizationOperation(final T identifier, final DataNodeContainer schema,
151 final DataSchemaNode node) {
152 super(identifier,node);
153 this.schema = schema;
154 this.byArg = new ConcurrentHashMap<>();
155 this.byQName = new ConcurrentHashMap<>();
159 public DataNormalizationOperation<?> getChild(final PathArgument child) throws DataNormalizationException {
160 DataNormalizationOperation<?> potential = byArg.get(child);
161 if (potential != null) {
164 potential = fromLocalSchema(child);
165 return register(potential);
169 public DataNormalizationOperation<?> getChild(final QName child) throws DataNormalizationException {
170 DataNormalizationOperation<?> potential = byQName.get(child);
171 if (potential != null) {
174 potential = fromLocalSchemaAndQName(schema, child);
175 return register(potential);
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 protected 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, schema);
227 @SuppressFBWarnings("BC_UNCONFIRMED_CAST")
228 public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
229 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = Builders
230 .mapEntryBuilder().withNodeIdentifier((NodeIdentifierWithPredicates) currentArg);
231 for (final Entry<QName, Object> keyValue : ((NodeIdentifierWithPredicates) currentArg).entrySet()) {
232 builder.addChild(Builders.leafBuilder()
234 .withNodeIdentifier(new NodeIdentifier(keyValue.getKey())).withValue(keyValue.getValue())
237 return builder.build();
241 public boolean isKeyedEntry() {
246 private static final class UnkeyedListItemNormalization
247 extends DataContainerNormalizationOperation<NodeIdentifier> {
248 UnkeyedListItemNormalization(final ListSchemaNode schema) {
249 super(new NodeIdentifier(schema.getQName()), schema,schema);
253 @SuppressFBWarnings("BC_UNCONFIRMED_CAST")
254 public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
255 return Builders.unkeyedListEntryBuilder().withNodeIdentifier((NodeIdentifier) currentArg).build();
259 private static final class ContainerNormalization extends DataContainerNormalizationOperation<NodeIdentifier> {
260 ContainerNormalization(final ContainerSchemaNode schema) {
261 super(new NodeIdentifier(schema.getQName()),schema, schema);
265 @SuppressFBWarnings("BC_UNCONFIRMED_CAST")
266 public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
267 return Builders.containerBuilder().withNodeIdentifier((NodeIdentifier) currentArg).build();
271 private abstract static class MixinNormalizationOp<T extends PathArgument>
272 extends CompositeNodeNormalizationOperation<T> {
274 MixinNormalizationOp(final T identifier, final DataSchemaNode schema) {
275 super(identifier,schema);
279 public final boolean isMixin() {
284 private static final class OrderedLeafListMixinNormalization extends UnorderedLeafListMixinNormalization {
285 OrderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
290 public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
291 return Builders.orderedLeafSetBuilder().withNodeIdentifier(getIdentifier()).build();
295 private static class UnorderedLeafListMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
297 private final DataNormalizationOperation<?> innerOp;
299 UnorderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
300 super(new NodeIdentifier(potential.getQName()),potential);
301 innerOp = new LeafListEntryNormalization(potential);
305 public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
306 return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier()).build();
310 public DataNormalizationOperation<?> getChild(final PathArgument child) {
311 if (child instanceof NodeWithValue) {
318 public DataNormalizationOperation<?> getChild(final QName child) {
319 if (getIdentifier().getNodeType().equals(child)) {
326 private static final class AugmentationNormalization
327 extends DataContainerNormalizationOperation<AugmentationIdentifier> {
329 AugmentationNormalization(final AugmentationSchemaNode augmentation, final DataNodeContainer schema) {
330 super(augmentationIdentifierFrom(augmentation), augmentationProxy(augmentation,schema),null);
333 private static DataNodeContainer augmentationProxy(final AugmentationSchemaNode augmentation,
334 final DataNodeContainer schema) {
335 final Set<DataSchemaNode> children = new HashSet<>();
336 for (final DataSchemaNode augNode : augmentation.getChildNodes()) {
337 children.add(schema.getDataChildByName(augNode.getQName()));
339 return new EffectiveAugmentationSchema(augmentation, children);
343 public boolean isMixin() {
348 protected DataNormalizationOperation<?> fromLocalSchemaAndQName(final DataNodeContainer schema,
350 final DataSchemaNode result = findChildSchemaNode(schema, child);
351 if (result == null) {
355 // We try to look up if this node was added by augmentation
356 if (schema instanceof DataSchemaNode && result.isAugmenting()) {
357 return fromAugmentation(schema, (AugmentationTarget) schema, result);
359 return fromDataSchemaNode(result);
363 protected Set<QName> getQNameIdentifiers() {
364 return getIdentifier().getPossibleChildNames();
368 public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
369 return Builders.augmentationBuilder().withNodeIdentifier(getIdentifier()).build();
373 private static class UnorderedMapMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
374 private final ListItemNormalization innerNode;
376 UnorderedMapMixinNormalization(final ListSchemaNode list) {
377 super(new NodeIdentifier(list.getQName()),list);
378 this.innerNode = new ListItemNormalization(NodeIdentifierWithPredicates.of(list.getQName(),
379 Collections.<QName, Object>emptyMap()), list);
383 public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
384 return Builders.mapBuilder().withNodeIdentifier(getIdentifier()).build();
388 public DataNormalizationOperation<?> getChild(final PathArgument child) {
389 if (child.getNodeType().equals(getIdentifier().getNodeType())) {
396 public DataNormalizationOperation<?> getChild(final QName child) {
397 if (getIdentifier().getNodeType().equals(child)) {
404 private static class UnkeyedListMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
405 private final UnkeyedListItemNormalization innerNode;
407 UnkeyedListMixinNormalization(final ListSchemaNode list) {
408 super(new NodeIdentifier(list.getQName()),list);
409 this.innerNode = new UnkeyedListItemNormalization(list);
413 public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
414 return Builders.unkeyedListBuilder().withNodeIdentifier(getIdentifier()).build();
418 public DataNormalizationOperation<?> getChild(final PathArgument child) {
419 if (child.getNodeType().equals(getIdentifier().getNodeType())) {
426 public DataNormalizationOperation<?> getChild(final QName child) {
427 if (getIdentifier().getNodeType().equals(child)) {
434 private static final class OrderedMapMixinNormalization extends UnorderedMapMixinNormalization {
435 OrderedMapMixinNormalization(final ListSchemaNode list) {
440 public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
441 return Builders.orderedMapBuilder().withNodeIdentifier(getIdentifier()).build();
445 private static class ChoiceNodeNormalization extends MixinNormalizationOp<NodeIdentifier> {
446 private final ImmutableMap<QName, DataNormalizationOperation<?>> byQName;
447 private final ImmutableMap<PathArgument, DataNormalizationOperation<?>> byArg;
449 ChoiceNodeNormalization(final ChoiceSchemaNode schema) {
450 super(new NodeIdentifier(schema.getQName()),schema);
451 final ImmutableMap.Builder<QName, DataNormalizationOperation<?>> byQNameBuilder = ImmutableMap.builder();
452 final ImmutableMap.Builder<PathArgument, DataNormalizationOperation<?>> byArgBuilder =
453 ImmutableMap.builder();
455 for (final CaseSchemaNode caze : schema.getCases().values()) {
456 for (final DataSchemaNode cazeChild : caze.getChildNodes()) {
457 final DataNormalizationOperation<?> childOp = fromDataSchemaNode(cazeChild);
458 byArgBuilder.put(childOp.getIdentifier(), childOp);
459 for (final QName qname : childOp.getQNameIdentifiers()) {
460 byQNameBuilder.put(qname, childOp);
464 byQName = byQNameBuilder.build();
465 byArg = byArgBuilder.build();
469 public DataNormalizationOperation<?> getChild(final PathArgument child) {
470 return byArg.get(child);
474 public DataNormalizationOperation<?> getChild(final QName child) {
475 return byQName.get(child);
479 public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
480 return Builders.choiceBuilder().withNodeIdentifier(getIdentifier()).build();
484 private static class AnyxmlNormalization extends DataNormalizationOperation<NodeIdentifier> {
485 AnyxmlNormalization(final AnyxmlSchemaNode schema) {
486 super(new NodeIdentifier(schema.getQName()), schema);
490 public DataNormalizationOperation<?> getChild(final PathArgument child) {
495 public DataNormalizationOperation<?> getChild(final QName child) {
500 public boolean isLeaf() {
505 public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
510 private static @Nullable DataSchemaNode findChildSchemaNode(final DataNodeContainer parent, final QName child) {
511 final DataSchemaNode potential = parent.getDataChildByName(child);
512 return potential != null ? potential : findChoice(parent, child);
515 private static @Nullable ChoiceSchemaNode findChoice(final DataNodeContainer parent, final QName child) {
516 for (final ChoiceSchemaNode choice : Iterables.filter(parent.getChildNodes(), ChoiceSchemaNode.class)) {
517 for (final CaseSchemaNode caze : choice.getCases().values()) {
518 if (findChildSchemaNode(caze, child) != null) {
526 public static AugmentationIdentifier augmentationIdentifierFrom(final AugmentationSchemaNode augmentation) {
527 final ImmutableSet.Builder<QName> potentialChildren = ImmutableSet.builder();
528 for (final DataSchemaNode child : augmentation.getChildNodes()) {
529 potentialChildren.add(child.getQName());
531 return new AugmentationIdentifier(potentialChildren.build());
535 * Returns a DataNormalizationOperation for provided child node.
538 * If supplied child is added by Augmentation this operation returns
539 * a DataNormalizationOperation for augmentation,
540 * otherwise returns a DataNormalizationOperation for child as
541 * call for {@link #fromDataSchemaNode(DataSchemaNode)}.
543 @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
544 justification = "https://github.com/spotbugs/spotbugs/issues/811")
545 private static DataNormalizationOperation<?> fromAugmentation(final DataNodeContainer parent,
546 final AugmentationTarget parentAug, final DataSchemaNode child) {
547 AugmentationSchemaNode augmentation = null;
548 for (final AugmentationSchemaNode aug : parentAug.getAvailableAugmentations()) {
549 final DataSchemaNode potential = aug.getDataChildByName(child.getQName());
550 if (potential != null) {
556 if (augmentation != null) {
557 return new AugmentationNormalization(augmentation, parent);
559 return fromDataSchemaNode(child);
563 public static DataNormalizationOperation<?> fromDataSchemaNode(final DataSchemaNode potential) {
564 if (potential instanceof ContainerSchemaNode) {
565 return new ContainerNormalization((ContainerSchemaNode) potential);
566 } else if (potential instanceof ListSchemaNode) {
568 return fromListSchemaNode((ListSchemaNode) potential);
569 } else if (potential instanceof LeafSchemaNode) {
570 return new LeafNormalization((LeafSchemaNode) potential);
571 } else if (potential instanceof ChoiceSchemaNode) {
572 return new ChoiceNodeNormalization((ChoiceSchemaNode) potential);
573 } else if (potential instanceof LeafListSchemaNode) {
574 return fromLeafListSchemaNode((LeafListSchemaNode) potential);
575 } else if (potential instanceof AnyxmlSchemaNode) {
576 return new AnyxmlNormalization((AnyxmlSchemaNode) potential);
581 private static DataNormalizationOperation<?> fromListSchemaNode(final ListSchemaNode potential) {
582 final List<QName> keyDefinition = potential.getKeyDefinition();
583 if (keyDefinition == null || keyDefinition.isEmpty()) {
584 return new UnkeyedListMixinNormalization(potential);
586 if (potential.isUserOrdered()) {
587 return new OrderedMapMixinNormalization(potential);
589 return new UnorderedMapMixinNormalization(potential);
592 private static DataNormalizationOperation<?> fromLeafListSchemaNode(final LeafListSchemaNode potential) {
593 if (potential.isUserOrdered()) {
594 return new OrderedLeafListMixinNormalization(potential);
596 return new UnorderedLeafListMixinNormalization(potential);
600 public static DataNormalizationOperation<?> from(final SchemaContext ctx) {
601 return new ContainerNormalization(ctx);
604 public abstract NormalizedNode<?, ?> createDefault(PathArgument currentArg);