From 6b72fbbb97aecc7ea556fc41684773f62f0f8f59 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Thu, 30 Sep 2021 19:30:07 +0200 Subject: [PATCH] Populate ietf-restconf operations container The 'operations' container defined in ietf-restconf module has magic semantics: it needs to have an empty leaf for every RPC that is available in the EffectiveModelContext. WWe are hard-pressed here to make things work with our parser infrastructure, as we currently do not have hooks to do this kind of schema-tree specific magic. Luckily there is also a YANG extension in that same model, so we hijack that to hook onto to the container inference and modify it just in time and make the magic happen. JIRA: YANGTOOLS-1338 Change-Id: I9cf774a148f0e764940b9725f717fa0c60ef1bf9 Signed-off-by: Robert Varga --- .../model/ri/stmt/EffectiveStatements.java | 14 ++- .../model/ri/stmt/UndeclaredStatements.java | 7 ++ .../eff/UndeclaredLeafEffectiveStatement.java | 94 +++++++++++++++ parser/rfc8040-parser-support/pom.xml | 10 +- .../OperationsCreateLeafStatements.java | 111 ++++++++++++++++++ .../OperationsValidateModuleAction.java | 91 ++++++++++++++ .../parser/YangDataStatementSupport.java | 23 +++- .../rfc8040/parser/AbstractYangDataTest.java | 50 ++++++++ .../yangtools/rfc8040/parser/YT1338Test.java | 55 +++++++++ .../rfc8040/parser/YangDataExtensionTest.java | 50 ++------ .../src/test/resources/yt1338/foo.yang | 6 + .../reactor/InferredStatementContext.java | 1 + .../parser/stmt/reactor/OriginalStmtCtx.java | 1 + .../stmt/reactor/ReplicaStatementContext.java | 2 +- .../stmt/reactor/StatementContextBase.java | 16 ++- .../stmt/meta/ActionStatementSupport.java | 4 +- .../stmt/meta/LeafStatementSupport.java | 23 +++- .../stmt/meta/RpcStatementSupport.java | 4 +- .../type/AbstractTypeStatementSupport.java | 60 +++++++--- .../yang/parser/spi/meta/StmtContext.java | 10 +- 20 files changed, 545 insertions(+), 87 deletions(-) create mode 100644 model/yang-model-ri/src/main/java/org/opendaylight/yangtools/yang/model/ri/stmt/impl/eff/UndeclaredLeafEffectiveStatement.java create mode 100644 parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/OperationsCreateLeafStatements.java create mode 100644 parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/OperationsValidateModuleAction.java create mode 100644 parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/AbstractYangDataTest.java create mode 100644 parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/YT1338Test.java create mode 100644 parser/rfc8040-parser-support/src/test/resources/yt1338/foo.yang diff --git a/model/yang-model-ri/src/main/java/org/opendaylight/yangtools/yang/model/ri/stmt/EffectiveStatements.java b/model/yang-model-ri/src/main/java/org/opendaylight/yangtools/yang/model/ri/stmt/EffectiveStatements.java index 0aac30f91b..dc6590032b 100644 --- a/model/yang-model-ri/src/main/java/org/opendaylight/yangtools/yang/model/ri/stmt/EffectiveStatements.java +++ b/model/yang-model-ri/src/main/java/org/opendaylight/yangtools/yang/model/ri/stmt/EffectiveStatements.java @@ -275,6 +275,7 @@ import org.opendaylight.yangtools.yang.model.ri.stmt.impl.eff.SlimLeafListEffect import org.opendaylight.yangtools.yang.model.ri.stmt.impl.eff.TypedefEffectiveStatementImpl; import org.opendaylight.yangtools.yang.model.ri.stmt.impl.eff.UndeclaredCaseEffectiveStatement; import org.opendaylight.yangtools.yang.model.ri.stmt.impl.eff.UndeclaredInputEffectiveStatement; +import org.opendaylight.yangtools.yang.model.ri.stmt.impl.eff.UndeclaredLeafEffectiveStatement; import org.opendaylight.yangtools.yang.model.ri.stmt.impl.eff.UndeclaredOutputEffectiveStatement; import org.opendaylight.yangtools.yang.model.spi.meta.SubstatementIndexingException; @@ -563,10 +564,15 @@ public final class EffectiveStatements { public static LeafEffectiveStatement copyLeaf(final LeafEffectiveStatement original, final QName argument, final int flags) { - checkArgument(original instanceof AbstractLeafEffectiveStatement, "Unsupported original %s", original); - final var orig = (AbstractLeafEffectiveStatement) original; - return argument.equals(orig.getDeclared().argument()) ? new EmptyLeafEffectiveStatement(orig, flags) - : new RegularLeafEffectiveStatement(orig, argument, flags); + if (original instanceof AbstractLeafEffectiveStatement) { + final var orig = (AbstractLeafEffectiveStatement) original; + return argument.equals(orig.getDeclared().argument()) ? new EmptyLeafEffectiveStatement(orig, flags) + : new RegularLeafEffectiveStatement(orig, argument, flags); + } else if (original instanceof UndeclaredLeafEffectiveStatement) { + return new UndeclaredLeafEffectiveStatement((UndeclaredLeafEffectiveStatement) original, argument, flags); + } else { + throw new IllegalArgumentException("Unsupported original " + original); + } } public static LeafEffectiveStatement createLeaf(final LeafStatement declared, final QName argument, final int flags, diff --git a/model/yang-model-ri/src/main/java/org/opendaylight/yangtools/yang/model/ri/stmt/UndeclaredStatements.java b/model/yang-model-ri/src/main/java/org/opendaylight/yangtools/yang/model/ri/stmt/UndeclaredStatements.java index 71ce1f91b9..67a9625ef7 100644 --- a/model/yang-model-ri/src/main/java/org/opendaylight/yangtools/yang/model/ri/stmt/UndeclaredStatements.java +++ b/model/yang-model-ri/src/main/java/org/opendaylight/yangtools/yang/model/ri/stmt/UndeclaredStatements.java @@ -14,9 +14,11 @@ import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.CaseEffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.InputEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.LeafEffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.OutputEffectiveStatement; import org.opendaylight.yangtools.yang.model.ri.stmt.impl.eff.UndeclaredCaseEffectiveStatement; import org.opendaylight.yangtools.yang.model.ri.stmt.impl.eff.UndeclaredInputEffectiveStatement; +import org.opendaylight.yangtools.yang.model.ri.stmt.impl.eff.UndeclaredLeafEffectiveStatement; import org.opendaylight.yangtools.yang.model.ri.stmt.impl.eff.UndeclaredOutputEffectiveStatement; import org.opendaylight.yangtools.yang.model.spi.meta.SubstatementIndexingException; @@ -41,6 +43,11 @@ public final class UndeclaredStatements { return new UndeclaredInputEffectiveStatement(substatements, argument, flags); } + public static LeafEffectiveStatement createLeaf(final QName argument, final int flags, + final ImmutableList> substatements) { + return new UndeclaredLeafEffectiveStatement(argument, flags, substatements); + } + public static OutputEffectiveStatement createOutput(final QName argument, final int flags, final ImmutableList> substatements) throws SubstatementIndexingException { diff --git a/model/yang-model-ri/src/main/java/org/opendaylight/yangtools/yang/model/ri/stmt/impl/eff/UndeclaredLeafEffectiveStatement.java b/model/yang-model-ri/src/main/java/org/opendaylight/yangtools/yang/model/ri/stmt/impl/eff/UndeclaredLeafEffectiveStatement.java new file mode 100644 index 0000000000..adc6a6fc70 --- /dev/null +++ b/model/yang-model-ri/src/main/java/org/opendaylight/yangtools/yang/model/ri/stmt/impl/eff/UndeclaredLeafEffectiveStatement.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2021 PANTHEON.tech, s.r.o. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yangtools.yang.model.ri.stmt.impl.eff; + +import static java.util.Objects.requireNonNull; + +import com.google.common.collect.ImmutableList; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.LeafEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.LeafStatement; +import org.opendaylight.yangtools.yang.model.ri.type.ConcreteTypes; +import org.opendaylight.yangtools.yang.model.spi.meta.AbstractUndeclaredEffectiveStatement; +import org.opendaylight.yangtools.yang.model.spi.meta.EffectiveStatementMixins.DataSchemaNodeMixin; +import org.opendaylight.yangtools.yang.model.spi.meta.EffectiveStatementMixins.MandatoryMixin; +import org.opendaylight.yangtools.yang.model.spi.meta.EffectiveStatementMixins.MustConstraintMixin; + +public final class UndeclaredLeafEffectiveStatement extends AbstractUndeclaredEffectiveStatement + implements LeafEffectiveStatement, LeafSchemaNode, DataSchemaNodeMixin, + MandatoryMixin, MustConstraintMixin { + private static final VarHandle TYPE; + + static { + try { + TYPE = MethodHandles.lookup().findVarHandle(UndeclaredLeafEffectiveStatement.class, "type", + TypeDefinition.class); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new ExceptionInInitializerError(e); + } + } + + private final @NonNull Object substatements; + private final @NonNull QName argument; + private final int flags; + + @SuppressWarnings("unused") + private volatile TypeDefinition type; + + public UndeclaredLeafEffectiveStatement(final QName argument, final int flags, + final ImmutableList> substatements) { + this.argument = requireNonNull(argument); + this.flags = flags; + this.substatements = maskList(substatements); + } + + public UndeclaredLeafEffectiveStatement(final UndeclaredLeafEffectiveStatement original, final QName argument, + final int flags) { + this.argument = requireNonNull(argument); + this.flags = flags; + substatements = original.substatements; + } + + @Override + public QName argument() { + return argument; + } + + @Override + public int flags() { + return flags; + } + + @Override + public ImmutableList> effectiveSubstatements() { + return unmaskList(substatements); + } + + @Override + public LeafEffectiveStatement asEffectiveStatement() { + return this; + } + + @Override + public TypeDefinition getType() { + final var local = (TypeDefinition) TYPE.getAcquire(this); + return local != null ? local : loadType(); + } + + private TypeDefinition loadType() { + final var ret = ConcreteTypes.typeOf(this); + final var witness = (TypeDefinition) TYPE.compareAndExchangeRelease(this, null, ret); + return witness != null ? witness : ret; + } +} diff --git a/parser/rfc8040-parser-support/pom.xml b/parser/rfc8040-parser-support/pom.xml index 531b609fb8..4076bb1328 100644 --- a/parser/rfc8040-parser-support/pom.xml +++ b/parser/rfc8040-parser-support/pom.xml @@ -50,21 +50,19 @@ org.opendaylight.yangtools rfc8040-model-api - - org.opendaylight.yangtools - yang-parser-reactor - test + yang-repo-api + org.opendaylight.yangtools - yang-parser-rfc7950 + yang-parser-reactor test org.opendaylight.yangtools - yang-repo-api + yang-parser-rfc7950 test diff --git a/parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/OperationsCreateLeafStatements.java b/parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/OperationsCreateLeafStatements.java new file mode 100644 index 0000000000..8703cbddf8 --- /dev/null +++ b/parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/OperationsCreateLeafStatements.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2021 PANTHEON.tech, s.r.o. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yangtools.rfc8040.parser; + +import static com.google.common.base.Verify.verify; +import static com.google.common.base.Verify.verifyNotNull; +import static java.util.Objects.requireNonNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.YangStmtMapping; +import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement; +import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition; +import org.opendaylight.yangtools.yang.model.api.stmt.LeafEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.RpcStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.type.TypeDefinitions; +import org.opendaylight.yangtools.yang.parser.spi.ModuleNamespace; +import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceAction; +import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceContext; +import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.Prerequisite; +import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase; +import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport; +import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupportNamespace; +import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext; +import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable; + +/** + * Once we have identified the {@code operations} container we want to enrich, we need to identify all RPC statements + * in the reactor. For that we need that all sources complete {@link ModelProcessingPhase#FULL_DECLARATION} -- after + * which we are still not done, as not all those RPCs may make it to the resulting {@link EffectiveModelContext}. + */ +final class OperationsCreateLeafStatements implements InferenceAction { + private final List>> prereqs; + private final Mutable operations; + + private OperationsCreateLeafStatements(final Mutable operations, + final List>> prereqs) { + this.operations = requireNonNull(operations); + this.prereqs = requireNonNull(prereqs); + } + + static void applyTo(final StmtContext ietfRestconfModule, final Mutable operations) { + final var action = operations.newInferenceAction(ModelProcessingPhase.EFFECTIVE_MODEL); + action.mutatesEffectiveCtx(operations); + + final var prereqs = new ArrayList>>(); + // FIXME: this not accurate: we need all sources, not just modules + for (var module : ietfRestconfModule.getAllFromNamespace(ModuleNamespace.class).values()) { + if (!ietfRestconfModule.equals(module)) { + prereqs.add(action.requiresCtx((StmtContext)module, ModelProcessingPhase.EFFECTIVE_MODEL)); + } + } + + action.apply(new OperationsCreateLeafStatements(operations, prereqs)); + } + + @Override + public void apply(final InferenceContext ctx) { + // Enumerate all RPCs that can be built + final var qnames = prereqs.stream() + .flatMap(prereq -> prereq.resolve(ctx).declaredSubstatements().stream()) + .filter(stmt -> stmt.producesDeclared(RpcStatement.class) + && stmt.isSupportedToBuildEffective() && stmt.isSupportedByFeatures()) + .map(stmt -> (QName) stmt.argument()) + // predictable order... + .sorted(Comparator.naturalOrder()) + // each QName should be distinct, but let's make sure anyway + .distinct() + .collect(Collectors.toUnmodifiableList()); + + if (!qnames.isEmpty()) { + final var leafSupport = getSupport(YangStmtMapping.LEAF, LeafEffectiveStatement.class); + final var typeSupport = getSupport(YangStmtMapping.TYPE, TypeEffectiveStatement.class); + + for (var qname : qnames) { + final var leaf = operations.createUndeclaredSubstatement(leafSupport, qname); + leaf.addEffectiveSubstatement(leaf.createUndeclaredSubstatement(typeSupport, TypeDefinitions.EMPTY)); + operations.addEffectiveSubstatement(leaf); + } + } + } + + @Override + public void prerequisiteFailed(final Collection> failed) { + // We do not really need to fail, as this means reactor will fail anyway + } + + private , Z extends EffectiveStatement> + StatementSupport getSupport(final StatementDefinition def, final Class effectiveClass) { + final var tmp = verifyNotNull(operations.getFromNamespace(StatementSupportNamespace.class, + def.getStatementName())); + final var repr = tmp.getEffectiveRepresentationClass(); + verify(effectiveClass.equals(repr), "Unexpected support %s representation %s", tmp, repr); + + @SuppressWarnings("unchecked") + final var ret = (StatementSupport) tmp; + return ret; + } +} diff --git a/parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/OperationsValidateModuleAction.java b/parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/OperationsValidateModuleAction.java new file mode 100644 index 0000000000..80ae818550 --- /dev/null +++ b/parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/OperationsValidateModuleAction.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2021 PANTHEON.tech, s.r.o. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yangtools.rfc8040.parser; + +import static java.util.Objects.requireNonNull; + +import java.util.Collection; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.yangtools.rfc8040.model.api.YangDataConstants; +import org.opendaylight.yangtools.yang.common.QNameModule; +import org.opendaylight.yangtools.yang.model.api.stmt.ContainerStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.GroupingStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement; +import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceAction; +import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceContext; +import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.Prerequisite; +import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase; +import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable; +import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName; + +/** + * An {@link InferenceAction} tasked with identifying when we are dealing with {@link YangDataConstants#RFC8040_SOURCE}. + */ +final class OperationsValidateModuleAction implements InferenceAction { + private static final String IETF_RESTCONF = YangDataConstants.RFC8040_SOURCE.getName(); + + private final Prerequisite> prereq; + + private OperationsValidateModuleAction(final Prerequisite> prereq) { + this.prereq = requireNonNull(prereq); + } + + static void applyTo(@NonNull final Mutable module) { + // Quick checks we can + if (module.producesDeclared(ModuleStatement.class) && IETF_RESTCONF.equals(module.rawArgument())) { + // This is 'yang-api' definition within a 'ietf-restconf' module, but we are not certain about revisions + // and its structure. Next up we require the module to be fully declared, hence an inference action is + // needed to continue this process. + final var action = module.newInferenceAction(ModelProcessingPhase.FULL_DECLARATION); + final var prereq = action.mutatesEffectiveCtx(module); + + action.apply(new OperationsValidateModuleAction(prereq)); + } + } + + @Override + public void apply(final InferenceContext ctx) { + final Mutable moduleCtx = prereq.resolve(ctx); + + // Check namespace and revision first + final QNameModule moduleQName = moduleCtx.getFromNamespace(ModuleCtxToModuleQName.class, moduleCtx); + if (!YangDataConstants.RFC8040_MODULE.equals(moduleQName)) { + return; + } + + // Now carefully locate the operations container: + // + // grouping restconf { + // container restconf { + // container operations; + // } + // } + // + for (var moduleSub : moduleCtx.mutableDeclaredSubstatements()) { + if (moduleSub.producesDeclared(GroupingStatement.class) && "restconf".equals(moduleSub.rawArgument())) { + for (var grpSub : moduleSub.mutableDeclaredSubstatements()) { + if (grpSub.producesDeclared(ContainerStatement.class) && "restconf".equals(grpSub.rawArgument())) { + for (var contSub : grpSub.mutableDeclaredSubstatements()) { + if (contSub.producesDeclared(ContainerStatement.class) + && "operations".equals(contSub.rawArgument())) { + // Alright, we have a match. Hook the second stage of processing. + OperationsCreateLeafStatements.applyTo(moduleCtx, contSub); + return; + } + } + } + } + } + } + } + + @Override + public void prerequisiteFailed(final Collection> failed) { + // We do not really need to fail, as this means reactor will fail anyway + } +} diff --git a/parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/YangDataStatementSupport.java b/parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/YangDataStatementSupport.java index f746ca1c69..6c18978c6c 100644 --- a/parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/YangDataStatementSupport.java +++ b/parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/YangDataStatementSupport.java @@ -21,6 +21,7 @@ import org.opendaylight.yangtools.yang.model.api.meta.DeclarationReference; import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement; import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.UsesEffectiveStatement; import org.opendaylight.yangtools.yang.parser.api.YangParserConfiguration; import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStringStatementSupport; import org.opendaylight.yangtools.yang.parser.spi.meta.BoundStmtCtx; @@ -55,10 +56,30 @@ public final class YangDataStatementSupport @Override public void onFullDefinitionDeclared(final Mutable ctx) { - // Parse and populate our argument to be picked up when we build the effecitve statement + // Parse and populate our argument to be picked up when we build the effective statement final String argument = SourceException.throwIfNull(ctx.argument(), ctx, "yang-data requires an argument"); final QName qname = StmtContextUtils.parseIdentifier(ctx, argument); ctx.addToNs(YangDataArgumentNamespace.class, Empty.value(), qname); + + // Support for 'operations' container semantics. For this we need to recognize when the model at hand matches + // RFC8040 ietf-restconf module. In ordered to do that we hook onto this particular definition: + // + // rc:yang-data yang-api { + // uses restconf; + // } + // + // If we find it, we hook an inference action which performs the next step when the module is fully declared. + if (ctx.isSupportedToBuildEffective() && "yang-api".equals(ctx.argument())) { + final var stmts = ctx.declaredSubstatements(); + if (stmts.size() == 1) { + final var stmt = stmts.iterator().next(); + if (stmt.producesEffective(UsesEffectiveStatement.class) && "restconf".equals(stmt.rawArgument())) { + // The rc:yang-data shape matches, but we are not sure about the module identity, that needs to be + // done later multiple stages, the first one being initiated through this call. + OperationsValidateModuleAction.applyTo(ctx.coerceParentContext()); + } + } + } } @Override diff --git a/parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/AbstractYangDataTest.java b/parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/AbstractYangDataTest.java new file mode 100644 index 0000000000..5a67d59727 --- /dev/null +++ b/parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/AbstractYangDataTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 PANTHEON.tech, s.r.o. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yangtools.rfc8040.parser; + +import java.io.IOException; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; +import org.opendaylight.yangtools.yang.parser.api.YangParserConfiguration; +import org.opendaylight.yangtools.yang.parser.api.YangSyntaxErrorException; +import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors; +import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangStatementStreamSource; +import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase; +import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource; +import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor; + +public abstract class AbstractYangDataTest { + static final StatementStreamSource IETF_RESTCONF_MODULE = sourceForResource( + "/yang-data-extension-test/ietf-restconf.yang"); + + static CrossSourceStatementReactor REACTOR; + + @BeforeClass + public static void createReactor() { + REACTOR = RFC7950Reactors.vanillaReactorBuilder() + .addNamespaceSupport(ModelProcessingPhase.FULL_DECLARATION, YangDataArgumentNamespace.BEHAVIOUR) + .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION, + new YangDataStatementSupport(YangParserConfiguration.DEFAULT)) + .build(); + } + + @AfterClass + public static void freeReactor() { + REACTOR = null; + } + + static StatementStreamSource sourceForResource(final String resourceName) { + try { + return YangStatementStreamSource.create(YangTextSchemaSource.forResource(resourceName)); + } catch (IOException | YangSyntaxErrorException e) { + throw new IllegalArgumentException("Failed to create source", e); + } + } + +} diff --git a/parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/YT1338Test.java b/parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/YT1338Test.java new file mode 100644 index 0000000000..93754121e3 --- /dev/null +++ b/parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/YT1338Test.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 PANTHEON.tech, s.r.o. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yangtools.rfc8040.parser; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.junit.Test; +import org.opendaylight.yangtools.rfc8040.model.api.YangDataConstants; +import org.opendaylight.yangtools.rfc8040.model.api.YangDataEffectiveStatement; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.model.api.meta.StatementOrigin; +import org.opendaylight.yangtools.yang.model.api.stmt.ContainerEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.LeafEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.type.TypeDefinitions; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; + +public class YT1338Test extends AbstractYangDataTest { + @Test + public void testAddedLeaves() throws ReactorException { + final var restconf = REACTOR.newBuild().addSources(IETF_RESTCONF_MODULE, sourceForResource("/yt1338/foo.yang")) + .buildEffective() + .findModuleStatement(YangDataConstants.RFC8040_MODULE) + .orElseThrow() + .streamEffectiveSubstatements(YangDataEffectiveStatement.class) + .filter(stmt -> "yang-api".equals(stmt.argument())) + .findAny() + .orElseThrow() + .findDataTreeNode(QName.create(YangDataConstants.RFC8040_MODULE, "restconf")) + .orElseThrow(); + assertThat(restconf, instanceOf(ContainerEffectiveStatement.class)); + + final var leaf = ((ContainerEffectiveStatement) restconf) + .findDataTreeNode(QName.create(YangDataConstants.RFC8040_MODULE, "operations")) + .orElseThrow() + .findFirstEffectiveSubstatement(LeafEffectiveStatement.class) + .orElseThrow(); + assertEquals(QName.create("foo", "foo"), leaf.argument()); + assertEquals(StatementOrigin.CONTEXT, leaf.statementOrigin()); + assertNull(leaf.getDeclared()); + + final var type = leaf.findFirstEffectiveSubstatement(TypeEffectiveStatement.class).orElseThrow(); + assertEquals(TypeDefinitions.EMPTY, type.argument()); + assertEquals(StatementOrigin.CONTEXT, type.statementOrigin()); + assertNull(type.getDeclared()); + } +} diff --git a/parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/YangDataExtensionTest.java b/parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/YangDataExtensionTest.java index ef8beeabdd..f06eb4cf75 100644 --- a/parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/YangDataExtensionTest.java +++ b/parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/YangDataExtensionTest.java @@ -16,11 +16,8 @@ import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import com.google.common.collect.ImmutableSet; -import java.io.IOException; import java.util.Collection; import java.util.Optional; -import org.junit.AfterClass; -import org.junit.BeforeClass; import org.junit.Test; import org.opendaylight.yangtools.rfc8040.model.api.YangDataSchemaNode; import org.opendaylight.yangtools.yang.common.QName; @@ -32,20 +29,13 @@ import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode; -import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; -import org.opendaylight.yangtools.yang.parser.api.YangParserConfiguration; -import org.opendaylight.yangtools.yang.parser.api.YangSyntaxErrorException; -import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors; -import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangStatementStreamSource; import org.opendaylight.yangtools.yang.parser.spi.meta.InvalidSubstatementException; import org.opendaylight.yangtools.yang.parser.spi.meta.MissingSubstatementException; -import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase; import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource; -import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor; import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor.BuildAction; -public class YangDataExtensionTest { +public class YangDataExtensionTest extends AbstractYangDataTest { private static final StatementStreamSource FOO_MODULE = sourceForResource( "/yang-data-extension-test/foo.yang"); @@ -61,33 +51,15 @@ public class YangDataExtensionTest { "/yang-data-extension-test/baz.yang"); private static final StatementStreamSource FOOBAR_MODULE = sourceForResource( "/yang-data-extension-test/foobar.yang"); - private static final StatementStreamSource IETF_RESTCONF_MODULE = sourceForResource( - "/yang-data-extension-test/ietf-restconf.yang"); private static final Revision REVISION = Revision.of("2017-06-01"); private static final QNameModule FOO_QNAMEMODULE = QNameModule.create(XMLNamespace.of("foo"), REVISION); private static final QName MY_YANG_DATA_A = QName.create(FOO_QNAMEMODULE, "my-yang-data-a"); private static final QName MY_YANG_DATA_B = QName.create(FOO_QNAMEMODULE, "my-yang-data-b"); - private static CrossSourceStatementReactor reactor; - - @BeforeClass - public static void createReactor() { - reactor = RFC7950Reactors.vanillaReactorBuilder() - .addNamespaceSupport(ModelProcessingPhase.FULL_DECLARATION, YangDataArgumentNamespace.BEHAVIOUR) - .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION, - new YangDataStatementSupport(YangParserConfiguration.DEFAULT)) - .build(); - } - - @AfterClass - public static void freeReactor() { - reactor = null; - } - @Test public void testYangData() throws Exception { - final SchemaContext schemaContext = reactor.newBuild().addSources(FOO_MODULE, IETF_RESTCONF_MODULE) + final SchemaContext schemaContext = REACTOR.newBuild().addSources(FOO_MODULE, IETF_RESTCONF_MODULE) .buildEffective(); assertNotNull(schemaContext); @@ -119,7 +91,7 @@ public class YangDataExtensionTest { @Test public void testConfigStatementBeingIgnoredInYangDataBody() throws Exception { - final SchemaContext schemaContext = reactor.newBuild().addSources(BAZ_MODULE, IETF_RESTCONF_MODULE) + final SchemaContext schemaContext = REACTOR.newBuild().addSources(BAZ_MODULE, IETF_RESTCONF_MODULE) .buildEffective(); assertNotNull(schemaContext); @@ -147,7 +119,7 @@ public class YangDataExtensionTest { @Test public void testIfFeatureStatementBeingIgnoredInYangDataBody() throws Exception { - final SchemaContext schemaContext = reactor.newBuild().setSupportedFeatures(ImmutableSet.of()) + final SchemaContext schemaContext = REACTOR.newBuild().setSupportedFeatures(ImmutableSet.of()) .addSources(FOOBAR_MODULE, IETF_RESTCONF_MODULE).buildEffective(); assertNotNull(schemaContext); @@ -174,7 +146,7 @@ public class YangDataExtensionTest { public void testYangDataBeingIgnored() throws Exception { // yang-data statement is ignored if it does not appear as a top-level statement // i.e., it will not appear in the final SchemaContext - final SchemaContext schemaContext = reactor.newBuild().addSources(BAR_MODULE, IETF_RESTCONF_MODULE) + final SchemaContext schemaContext = REACTOR.newBuild().addSources(BAR_MODULE, IETF_RESTCONF_MODULE) .buildEffective(); assertNotNull(schemaContext); @@ -192,7 +164,7 @@ public class YangDataExtensionTest { @Test public void testYangDataWithMissingTopLevelContainer() { - final BuildAction build = reactor.newBuild().addSources(FOO_INVALID_1_MODULE, IETF_RESTCONF_MODULE); + final BuildAction build = REACTOR.newBuild().addSources(FOO_INVALID_1_MODULE, IETF_RESTCONF_MODULE); final ReactorException ex = assertThrows(ReactorException.class, () -> build.buildEffective()); final Throwable cause = ex.getCause(); assertThat(cause, instanceOf(MissingSubstatementException.class)); @@ -201,18 +173,10 @@ public class YangDataExtensionTest { @Test public void testYangDataWithTwoTopLevelContainers() { - final BuildAction build = reactor.newBuild().addSources(FOO_INVALID_2_MODULE, IETF_RESTCONF_MODULE); + final BuildAction build = REACTOR.newBuild().addSources(FOO_INVALID_2_MODULE, IETF_RESTCONF_MODULE); final ReactorException ex = assertThrows(ReactorException.class, () -> build.buildEffective()); final Throwable cause = ex.getCause(); assertThat(cause, instanceOf(InvalidSubstatementException.class)); assertThat(cause.getMessage(), startsWith("yang-data requires exactly one data definition node, found 2")); } - - private static StatementStreamSource sourceForResource(final String resourceName) { - try { - return YangStatementStreamSource.create(YangTextSchemaSource.forResource(resourceName)); - } catch (IOException | YangSyntaxErrorException e) { - throw new IllegalArgumentException("Failed to create source", e); - } - } } diff --git a/parser/rfc8040-parser-support/src/test/resources/yt1338/foo.yang b/parser/rfc8040-parser-support/src/test/resources/yt1338/foo.yang new file mode 100644 index 0000000000..669998c38a --- /dev/null +++ b/parser/rfc8040-parser-support/src/test/resources/yt1338/foo.yang @@ -0,0 +1,6 @@ +module foo { + namespace foo; + prefix foo; + + rpc foo; +} diff --git a/parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/InferredStatementContext.java b/parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/InferredStatementContext.java index c0091937ab..b1693ae1d5 100644 --- a/parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/InferredStatementContext.java +++ b/parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/InferredStatementContext.java @@ -194,6 +194,7 @@ final class InferredStatementContext, E extend @Override public void addEffectiveSubstatement(final Mutable substatement) { substatements = addEffectiveSubstatement(ensureEffectiveSubstatements(), substatement); + afterAddEffectiveSubstatement(substatement); } @Override diff --git a/parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/OriginalStmtCtx.java b/parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/OriginalStmtCtx.java index c4c7d95c88..7492ddd2f7 100644 --- a/parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/OriginalStmtCtx.java +++ b/parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/OriginalStmtCtx.java @@ -86,6 +86,7 @@ abstract class OriginalStmtCtx, E extends Effe @Override public final void addEffectiveSubstatement(final Mutable substatement) { effective = addEffectiveSubstatement(effective, substatement); + afterAddEffectiveSubstatement(substatement); } @Override diff --git a/parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ReplicaStatementContext.java b/parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ReplicaStatementContext.java index f74451c17c..5843492618 100644 --- a/parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ReplicaStatementContext.java +++ b/parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ReplicaStatementContext.java @@ -187,7 +187,7 @@ final class ReplicaStatementContext, E extends @Override @Deprecated public , Z extends EffectiveStatement> - Mutable addUndeclaredSubstatement(final StatementSupport support, final X arg) { + Mutable createUndeclaredSubstatement(final StatementSupport support, final X arg) { throw new UnsupportedOperationException(); } diff --git a/parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java b/parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java index c7bd79a01a..438c6e6365 100644 --- a/parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java +++ b/parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java @@ -331,13 +331,12 @@ abstract class StatementContextBase, E extends @Override public final , Z extends EffectiveStatement> - Mutable addUndeclaredSubstatement(final StatementSupport support, final X arg) { + Mutable createUndeclaredSubstatement(final StatementSupport support, final X arg) { requireNonNull(support); checkArgument(support instanceof UndeclaredStatementFactory, "Unsupported statement support %s", support); final var ret = new UndeclaredStmtCtx<>(this, support, arg); support.onStatementAdded(ret); - addEffectiveSubstatement(ret); return ret; } @@ -352,6 +351,19 @@ abstract class StatementContextBase, E extends return resized; } + static final void afterAddEffectiveSubstatement(final Mutable substatement) { + // Undeclared statements still need to have 'onDeclarationFinished()' triggered + if (substatement instanceof UndeclaredStmtCtx) { + finishDeclaration((UndeclaredStmtCtx) substatement); + } + } + + // Split out to keep generics working without a warning + private static , Z extends EffectiveStatement> void finishDeclaration( + final UndeclaredStmtCtx substatement) { + substatement.definition().onDeclarationFinished(substatement, ModelProcessingPhase.FULL_DECLARATION); + } + @Override public final void addEffectiveSubstatements(final Collection> statements) { if (!statements.isEmpty()) { diff --git a/parser/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/meta/ActionStatementSupport.java b/parser/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/meta/ActionStatementSupport.java index 1d2d5c15e8..ca8e742625 100644 --- a/parser/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/meta/ActionStatementSupport.java +++ b/parser/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/meta/ActionStatementSupport.java @@ -132,7 +132,7 @@ public final class ActionStatementSupport extends private static void appendImplicitSubstatement(final Mutable stmt, final QName substatementName) { - stmt.addUndeclaredSubstatement( - verifyNotNull(stmt.getFromNamespace(StatementSupportNamespace.class, substatementName)), null); + stmt.addEffectiveSubstatement(stmt.createUndeclaredSubstatement( + verifyNotNull(stmt.getFromNamespace(StatementSupportNamespace.class, substatementName)), null)); } } diff --git a/parser/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/meta/LeafStatementSupport.java b/parser/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/meta/LeafStatementSupport.java index 037195e2bc..ff7073f91a 100644 --- a/parser/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/meta/LeafStatementSupport.java +++ b/parser/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/meta/LeafStatementSupport.java @@ -11,6 +11,7 @@ import static com.google.common.base.Verify.verify; import com.google.common.collect.ImmutableList; import java.util.Collection; +import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.Status; @@ -27,13 +28,14 @@ import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement; import org.opendaylight.yangtools.yang.model.ri.stmt.DeclaredStatementDecorators; import org.opendaylight.yangtools.yang.model.ri.stmt.DeclaredStatements; import org.opendaylight.yangtools.yang.model.ri.stmt.EffectiveStatements; +import org.opendaylight.yangtools.yang.model.ri.stmt.UndeclaredStatements; import org.opendaylight.yangtools.yang.model.spi.meta.EffectiveStatementMixins.EffectiveStatementWithFlags.FlagsBuilder; import org.opendaylight.yangtools.yang.parser.api.YangParserConfiguration; import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.EffectiveStmtUtils; -import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractSchemaTreeStatementSupport; import org.opendaylight.yangtools.yang.parser.spi.meta.BoundStmtCtx; import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStatementState; import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStmtCtx.Current; +import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStmtCtx.UndeclaredCurrent; import org.opendaylight.yangtools.yang.parser.spi.meta.QNameWithFlagsEffectiveStatementState; import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable; import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils; @@ -41,7 +43,7 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator; import org.opendaylight.yangtools.yang.parser.spi.source.SourceException; public final class LeafStatementSupport - extends AbstractSchemaTreeStatementSupport { + extends AbstractImplicitStatementSupport { private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping.LEAF) .addOptional(YangStmtMapping.CONFIG) @@ -88,6 +90,20 @@ public final class LeafStatementSupport @Override protected LeafEffectiveStatement createEffective(final Current stmt, final ImmutableList> substatements) { + validateEffective(stmt, substatements); + return EffectiveStatements.createLeaf(stmt.declared(), stmt.getArgument(), computeFlags(stmt, substatements), + substatements); + } + + @Override + LeafEffectiveStatement createUndeclaredEffective(final UndeclaredCurrent stmt, + final ImmutableList> substatements) { + validateEffective(stmt, substatements); + return UndeclaredStatements.createLeaf(stmt.getArgument(), computeFlags(stmt, substatements), substatements); + } + + private static void validateEffective(final @NonNull BoundStmtCtx stmt, + final ImmutableList> substatements) { final TypeEffectiveStatement typeStmt = SourceException.throwIfNull( findFirstStatement(substatements, TypeEffectiveStatement.class), stmt, "Leaf is missing a 'type' statement"); @@ -95,9 +111,6 @@ public final class LeafStatementSupport SourceException.throwIf( EffectiveStmtUtils.hasDefaultValueMarkedWithIfFeature(stmt.yangVersion(), typeStmt, dflt), stmt, "Leaf '%s' has default value '%s' marked with an if-feature statement.", stmt.argument(), dflt); - - return EffectiveStatements.createLeaf(stmt.declared(), stmt.getArgument(), computeFlags(stmt, substatements), - substatements); } @Override diff --git a/parser/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/meta/RpcStatementSupport.java b/parser/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/meta/RpcStatementSupport.java index b696325e4d..a228e35c6d 100644 --- a/parser/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/meta/RpcStatementSupport.java +++ b/parser/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/meta/RpcStatementSupport.java @@ -100,7 +100,7 @@ public final class RpcStatementSupport extends AbstractSchemaTreeStatementSuppor private static void appendImplicitSubstatement(final Mutable stmt, final QName substatementName) { - stmt.addUndeclaredSubstatement( - verifyNotNull(stmt.getFromNamespace(StatementSupportNamespace.class, substatementName)), null); + stmt.addEffectiveSubstatement(stmt.createUndeclaredSubstatement( + verifyNotNull(stmt.getFromNamespace(StatementSupportNamespace.class, substatementName)), null)); } } diff --git a/parser/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/AbstractTypeStatementSupport.java b/parser/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/AbstractTypeStatementSupport.java index 086f8d9f09..aea249a6ac 100644 --- a/parser/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/AbstractTypeStatementSupport.java +++ b/parser/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/AbstractTypeStatementSupport.java @@ -12,6 +12,8 @@ import static com.google.common.base.Verify.verifyNotNull; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import java.util.Collection; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.yangtools.yang.common.Decimal64; import org.opendaylight.yangtools.yang.common.Empty; @@ -71,6 +73,7 @@ import org.opendaylight.yangtools.yang.parser.spi.TypeNamespace; import org.opendaylight.yangtools.yang.parser.spi.meta.BoundStmtCtx; import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStmtCtx; import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStmtCtx.Current; +import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStmtCtx.UndeclaredCurrent; import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException; import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder; import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceAction; @@ -82,9 +85,11 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport; import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext; import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable; import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator; +import org.opendaylight.yangtools.yang.parser.spi.meta.UndeclaredStatementFactory; import org.opendaylight.yangtools.yang.parser.spi.source.SourceException; -abstract class AbstractTypeStatementSupport extends AbstractTypeSupport { +abstract class AbstractTypeStatementSupport extends AbstractTypeSupport + implements UndeclaredStatementFactory> { private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping.TYPE) .addOptional(YangStmtMapping.BASE) @@ -99,23 +104,23 @@ abstract class AbstractTypeStatementSupport extends AbstractTypeSupport STATIC_BUILT_IN_TYPES = - ImmutableMap.builder() - .put(TypeDefinitions.BINARY.getLocalName(), BuiltinEffectiveStatement.BINARY) - .put(TypeDefinitions.BOOLEAN.getLocalName(), BuiltinEffectiveStatement.BOOLEAN) - .put(TypeDefinitions.EMPTY.getLocalName(), BuiltinEffectiveStatement.EMPTY) + private static final ImmutableMap STATIC_BUILT_IN_TYPES = + ImmutableMap.builder() + .put(TypeDefinitions.BINARY, BuiltinEffectiveStatement.BINARY) + .put(TypeDefinitions.BOOLEAN, BuiltinEffectiveStatement.BOOLEAN) + .put(TypeDefinitions.EMPTY, BuiltinEffectiveStatement.EMPTY) // FIXME: this overlaps with DYNAMIC_BUILT_IN_TYPES. One of these is not needed, but we need to decide // what to do. I think we should gradually use per-statement validators, hence go towards dynamic? - .put(TypeDefinitions.INSTANCE_IDENTIFIER.getLocalName(), BuiltinEffectiveStatement.INSTANCE_IDENTIFIER) - .put(TypeDefinitions.INT8.getLocalName(), BuiltinEffectiveStatement.INT8) - .put(TypeDefinitions.INT16.getLocalName(), BuiltinEffectiveStatement.INT16) - .put(TypeDefinitions.INT32.getLocalName(), BuiltinEffectiveStatement.INT32) - .put(TypeDefinitions.INT64.getLocalName(), BuiltinEffectiveStatement.INT64) - .put(TypeDefinitions.STRING.getLocalName(), BuiltinEffectiveStatement.STRING) - .put(TypeDefinitions.UINT8.getLocalName(), BuiltinEffectiveStatement.UINT8) - .put(TypeDefinitions.UINT16.getLocalName(), BuiltinEffectiveStatement.UINT16) - .put(TypeDefinitions.UINT32.getLocalName(), BuiltinEffectiveStatement.UINT32) - .put(TypeDefinitions.UINT64.getLocalName(), BuiltinEffectiveStatement.UINT64) + .put(TypeDefinitions.INSTANCE_IDENTIFIER, BuiltinEffectiveStatement.INSTANCE_IDENTIFIER) + .put(TypeDefinitions.INT8, BuiltinEffectiveStatement.INT8) + .put(TypeDefinitions.INT16, BuiltinEffectiveStatement.INT16) + .put(TypeDefinitions.INT32, BuiltinEffectiveStatement.INT32) + .put(TypeDefinitions.INT64, BuiltinEffectiveStatement.INT64) + .put(TypeDefinitions.STRING, BuiltinEffectiveStatement.STRING) + .put(TypeDefinitions.UINT8, BuiltinEffectiveStatement.UINT8) + .put(TypeDefinitions.UINT16, BuiltinEffectiveStatement.UINT16) + .put(TypeDefinitions.UINT32, BuiltinEffectiveStatement.UINT32) + .put(TypeDefinitions.UINT64, BuiltinEffectiveStatement.UINT64) .build(); private final ImmutableMap> dynamicBuiltInTypes; @@ -138,13 +143,13 @@ abstract class AbstractTypeStatementSupport extends AbstractTypeSupport> stmt) { super.onFullDefinitionDeclared(stmt); - final BuiltinEffectiveStatement builtin = STATIC_BUILT_IN_TYPES.get(stmt.getRawArgument()); + final QName typeQName = stmt.getArgument(); + final BuiltinEffectiveStatement builtin = STATIC_BUILT_IN_TYPES.get(typeQName); if (builtin != null) { stmt.addToNs(BaseTypeNamespace.class, Empty.value(), builtin); return; } - final QName typeQName = stmt.getArgument(); final ModelActionBuilder typeAction = stmt.newInferenceAction(ModelProcessingPhase.EFFECTIVE_MODEL); final Prerequisite> typePrereq = typeAction.requiresCtx(stmt, TypeNamespace.class, typeQName, ModelProcessingPhase.EFFECTIVE_MODEL); @@ -178,6 +183,25 @@ abstract class AbstractTypeStatementSupport extends AbstractTypeSupport createUndeclaredEffective( + final UndeclaredCurrent stmt, + final Stream> effectiveSubstatements) { + final ImmutableList> substatements = buildEffectiveSubstatements(stmt, + statementsToBuild(stmt, effectiveSubstatements + .filter(StmtContext::isSupportedToBuildEffective) + .collect(Collectors.toUnmodifiableList()))); + + // First look up the proper base type + final TypeEffectiveStatement typeStmt = resolveType(stmt); + if (substatements.isEmpty()) { + return typeStmt; + } + + // TODO: mirror the logic below + throw new UnsupportedOperationException("Non-empty undeclared type statements are not implemented yet"); + } + @Override protected final TypeStatement createDeclared(final BoundStmtCtx ctx, final ImmutableList> substatements) { diff --git a/parser/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StmtContext.java b/parser/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StmtContext.java index 7d0528a952..d940675907 100644 --- a/parser/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StmtContext.java +++ b/parser/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StmtContext.java @@ -310,21 +310,25 @@ public interface StmtContext, E extends Effect void addEffectiveSubstatements(Collection> statements); /** - * Adds a purely-effective statement to collection of substatements. The statement will report a {@code null} + * Create a purely-effective substatement. The statement will report a {@code null} * {@link EffectiveStatement#getDeclared()} object. A typical example of statements which require this mechanics * are {@code rpc} and {@code action} statements, which always have {@code input} and {@code output} - * substatements, even if those are not declared in YANG text. + * substatements, even if those are not declared in YANG text. The returned context is not added to this + * context's substatements. That needs to done once the statement is completely defined through + * {@link #addEffectiveSubstatement(Mutable)} -- which will trigger + * {@link StatementSupport#onFullDefinitionDeclared(Mutable)}. * * @param support Statement support of the statement being created * @param arg Effective argument. If specified as {@code null}, statement support will be consulted for the * empty argument. + * @return A new statement * @throws IllegalArgumentException if {@code support} does not implement {@link UndeclaredStatementFactory} * @throws IllegalStateException if added in declared phase * @throws NullPointerException if {@code support} is null */ @Beta , Z extends EffectiveStatement> - @NonNull Mutable addUndeclaredSubstatement(StatementSupport support, @Nullable X arg); + @NonNull Mutable createUndeclaredSubstatement(StatementSupport support, @Nullable X arg); @Beta void removeStatementFromEffectiveSubstatements(StatementDefinition statementDef); -- 2.36.6