65373e2d4a395db92bfb893547658e57b59c593a
[yangtools.git] / data / yang-data-tree-ri / src / main / java / org / opendaylight / yangtools / yang / data / tree / impl / MandatoryDescendant.java
1 /*
2  * Copyright (c) 2021 PANTHEON.tech, s.r.o. 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.tree.impl;
9
10 import static com.google.common.base.Verify.verify;
11 import static java.util.Objects.requireNonNull;
12
13 import java.util.List;
14 import org.eclipse.jdt.annotation.NonNull;
15 import org.eclipse.jdt.annotation.Nullable;
16 import org.opendaylight.yangtools.concepts.Immutable;
17 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
18 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
20 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
21 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
22 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
23 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
25 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
26 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 /**
31  * A path to a descendant which must exist. This really should be equivalent to YangInstanceIdentifier, but for now we
32  * need to deal with up to two possible paths -- one with AugmentationIdentifiers and one without.
33  */
34 // FIXME: 8.0.0: remove this structure and just keep the 'path' YangInstanceIdentifier
35 final class MandatoryDescendant implements Immutable {
36     private static final Logger LOG = LoggerFactory.getLogger(MandatoryDescendant.class);
37
38     // Correctly-nested path, mirroring Augmentation and choice/case structure
39     private final @NonNull YangInstanceIdentifier path;
40     // Legacy trivial path,
41     private final @Nullable YangInstanceIdentifier legacyPath;
42
43     private MandatoryDescendant(final YangInstanceIdentifier path, final YangInstanceIdentifier legacyPath) {
44         this.path = requireNonNull(path);
45         this.legacyPath = legacyPath;
46     }
47
48     static @NonNull MandatoryDescendant create(final YangInstanceIdentifier parentId,
49             final DataNodeContainer parentSchema, final DataSchemaNode childSchema, final boolean inAugmentedSubtree) {
50         final NodeIdentifier childId = NodeIdentifier.create(childSchema.getQName());
51
52         if (childSchema.isAugmenting()) {
53             if (!inAugmentedSubtree) {
54                 final AugmentationSchemaNode aug = getAugIdentifierOfChild(parentSchema, childSchema);
55                 return new MandatoryDescendant(
56                     parentId.node(DataSchemaContextNode.augmentationIdentifierFrom(aug)).node(childId).toOptimized(),
57                     parentId.node(childId).toOptimized());
58             }
59
60             final List<PathArgument> augSubtreePath = parentId.getPathArguments();
61             // in case of augmented choice-case the pathArguments might be empty
62             final YangInstanceIdentifier legacyPath = !augSubtreePath.isEmpty()
63                 ? YangInstanceIdentifier.create(augSubtreePath.subList(1, augSubtreePath.size())) : null;
64             return new MandatoryDescendant(parentId.node(childId).toOptimized(), legacyPath);
65         }
66
67         return new MandatoryDescendant(parentId.node(childId).toOptimized(), null);
68     }
69
70     void enforceOnData(final NormalizedNode data) {
71         // Find try primary path first ...
72         if (NormalizedNodes.findNode(data, path).isPresent()) {
73             return;
74         }
75         // ... if we have a legacy path, try that as well ...
76         if (legacyPath != null && NormalizedNodes.findNode(data, legacyPath).isPresent()) {
77             // .. this should not really be happening ...
78             LOG.debug("Found {} at alternate path {}", path, legacyPath);
79             return;
80         }
81
82         // ... not found, report the error
83         throw new IllegalArgumentException(String.format("Node %s is missing mandatory descendant %s",
84             data.getIdentifier(), path));
85     }
86
87     static AugmentationSchemaNode getAugIdentifierOfChild(final DataNodeContainer parent, final DataSchemaNode child) {
88         verify(parent instanceof AugmentationTarget,
89             "Unexpected augmenting child %s in non-target parent %s", child, parent);
90         return ((AugmentationTarget) parent).getAvailableAugmentations().stream()
91             .filter(augment -> augment.findDataChildByName(child.getQName()).isPresent())
92             .findFirst()
93             .orElseThrow(() -> new IllegalArgumentException(String.format(
94                 "Node %s is marked as augmenting but is not present in the schema of %s", child.getQName(), parent)));
95     }
96
97     @Override
98     public String toString() {
99         return legacyPath == null ? path.toString() : "(" + path + " || " + legacyPath + ")";
100     }
101 }