2 * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc8040.parser;
10 import static com.google.common.base.Verify.verify;
12 import com.google.common.annotations.Beta;
13 import com.google.common.annotations.VisibleForTesting;
14 import com.google.common.collect.ImmutableList;
15 import java.util.stream.Collectors;
16 import org.eclipse.jdt.annotation.NonNull;
17 import org.opendaylight.yangtools.rfc8040.model.api.YangDataConstants;
18 import org.opendaylight.yangtools.rfc8040.model.api.YangDataEffectiveStatement;
19 import org.opendaylight.yangtools.rfc8040.model.api.YangDataStatement;
20 import org.opendaylight.yangtools.rfc8040.model.api.YangDataStatements;
21 import org.opendaylight.yangtools.yang.common.YangDataName;
22 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
23 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
24 import org.opendaylight.yangtools.yang.model.api.meta.DeclarationReference;
25 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
26 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
27 import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceEffectiveStatement;
28 import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeEffectiveStatement;
29 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
30 import org.opendaylight.yangtools.yang.model.api.stmt.UsesEffectiveStatement;
31 import org.opendaylight.yangtools.yang.parser.api.YangParserConfiguration;
32 import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
33 import org.opendaylight.yangtools.yang.parser.spi.meta.BoundStmtCtx;
34 import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStmtCtx.Current;
35 import org.opendaylight.yangtools.yang.parser.spi.meta.InvalidSubstatementException;
36 import org.opendaylight.yangtools.yang.parser.spi.meta.MissingSubstatementException;
37 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
38 import org.opendaylight.yangtools.yang.parser.spi.meta.ParserNamespace;
39 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
40 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
41 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
42 import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
43 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
46 public final class YangDataStatementSupport
47 extends AbstractStatementSupport<YangDataName, YangDataStatement, YangDataEffectiveStatement> {
48 private static final @NonNull ParserNamespace<YangDataName,
49 StmtContext<YangDataName, YangDataStatement, YangDataEffectiveStatement>> NAMESPACE =
50 new ParserNamespace<>("yang-data");
51 public static final @NonNull NamespaceBehaviour<YangDataName,
52 StmtContext<YangDataName, YangDataStatement, YangDataEffectiveStatement>> BEHAVIOUR =
53 NamespaceBehaviour.global(NAMESPACE);
55 // As per RFC8040 page 81:
57 // The substatements of this extension MUST follow the
58 // 'data-def-stmt' rule in the YANG ABNF.
60 // As per RFC7950 page 185:
62 // data-def-stmt = container-stmt /
71 // The cardinality is not exactly constrained, but the entirety of substatements are required to resolve to a single
72 // XML document (page 80). This is enforced when we arrive at full declaration.
73 private static final SubstatementValidator VALIDATOR = SubstatementValidator.builder(YangDataStatements.YANG_DATA)
74 .addAny(YangStmtMapping.CONTAINER)
75 .addAny(YangStmtMapping.LEAF)
76 .addAny(YangStmtMapping.LEAF_LIST)
77 .addAny(YangStmtMapping.LIST)
78 .addAny(YangStmtMapping.CHOICE)
79 .addAny(YangStmtMapping.ANYDATA)
80 .addAny(YangStmtMapping.ANYXML)
81 .addAny(YangStmtMapping.USES)
85 static final YangDataName YANG_API = new YangDataName(YangDataConstants.RFC8040_MODULE, "yang-api");
87 public YangDataStatementSupport(final YangParserConfiguration config) {
88 super(YangDataStatements.YANG_DATA, StatementPolicy.reject(), config, VALIDATOR);
92 public YangDataName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
94 return new YangDataName(StmtContextUtils.getModuleQName(ctx.getRoot()), value);
95 } catch (IllegalArgumentException e) {
96 throw new SourceException(ctx, e, "Invalid yang-data argument %s", value);
101 public void onStatementAdded(final Mutable<YangDataName, YangDataStatement, YangDataEffectiveStatement> stmt) {
102 // as per https://www.rfc-editor.org/rfc/rfc8040#section-8,
103 // yang-data is ignored unless it appears as a top-level statement
104 final var parent = stmt.coerceParentContext();
105 if (parent.getParentContext() != null) {
106 stmt.setUnsupported();
110 final var name = stmt.argument();
111 final var prev = parent.namespaceItem(NAMESPACE, name);
113 throw new SourceException(stmt,
114 "Error in module '%s': cannot add '%s'. Node name collision: '%s' already declared at %s",
115 stmt.getRoot().rawArgument(), name, prev.argument(), prev.sourceReference());
117 parent.addToNs(NAMESPACE, stmt.argument(), stmt);
121 public void onFullDefinitionDeclared(
122 final Mutable<YangDataName, YangDataStatement, YangDataEffectiveStatement> ctx) {
123 // If we are declared in an illegal place, this becomes a no-op
124 if (!ctx.isSupportedToBuildEffective()) {
128 // Run SubstatementValidator-based validation first
129 super.onFullDefinitionDeclared(ctx);
131 // Support for 'operations' container semantics. For this we need to recognize when the model at hand matches
132 // RFC8040 ietf-restconf module. In ordered to do that we hook onto this particular definition:
134 // rc:yang-data yang-api {
138 // If we find it, we hook an inference action which performs the next step when the module is fully declared.
139 if (YANG_API.equals(ctx.getArgument())) {
140 final var stmts = ctx.declaredSubstatements();
141 if (stmts.size() == 1) {
142 final var stmt = stmts.iterator().next();
143 if (stmt.producesEffective(UsesEffectiveStatement.class) && "restconf".equals(stmt.rawArgument())) {
144 // The rc:yang-data shape matches, but we are not sure about the module identity, that needs to be
145 // done later multiple stages, the first one being initiated through this call.
146 OperationsValidateModuleAction.applyTo(ctx.coerceParentContext());
153 public boolean isIgnoringIfFeatures() {
158 public boolean isIgnoringConfig() {
163 protected YangDataStatement createDeclared(final BoundStmtCtx<YangDataName> ctx,
164 final ImmutableList<DeclaredStatement<?>> substatements) {
165 return new YangDataStatementImpl(ctx.getArgument(), substatements);
169 protected YangDataStatement attachDeclarationReference(final YangDataStatement stmt,
170 final DeclarationReference reference) {
171 return new RefYangDataStatement(stmt, reference);
175 protected YangDataEffectiveStatement createEffective(final Current<YangDataName, YangDataStatement> stmt,
176 final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
177 // RFC8040 page 80 requires that:
178 // It MUST contain data definition statements
179 // that result in exactly one container data node definition.
180 // An instance of a YANG data template can thus be translated
181 // into an XML instance document, whose top-level element
182 // corresponds to the top-level container.
184 // We validate this additional constraint when we arrive at the effective model, with the view that
185 // 'container data node definition' is really meant to say 'XML element'.
187 // This really boils down to the requirement to have a single schema tree substatement, which needs to either
188 // be a data tree statement or a choice statement.
189 final var schemaSub = substatements.stream()
190 .filter(SchemaTreeEffectiveStatement.class::isInstance)
191 .map(SchemaTreeEffectiveStatement.class::cast)
192 .collect(Collectors.toUnmodifiableList());
193 final var child = switch (schemaSub.size()) {
194 case 0 -> throw new MissingSubstatementException(stmt, "yang-data requires at least one substatement");
196 final SchemaTreeEffectiveStatement<?> substmt = schemaSub.get(0);
197 SourceException.throwIf(
198 !(substmt instanceof ChoiceEffectiveStatement) && !(substmt instanceof DataTreeEffectiveStatement),
199 stmt, "%s is not a recognized container data node definition", substmt);
200 verify(substmt instanceof DataSchemaNode, "Unexpected single child %s", substmt);
201 yield (DataSchemaNode) substmt;
203 default -> throw new InvalidSubstatementException(stmt,
204 "yang-data requires exactly one container data node definition, found %s", schemaSub);
207 return new YangDataEffectiveStatementImpl(stmt, substatements, child);