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;
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,
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;
return new UndeclaredInputEffectiveStatement(substatements, argument, flags);
}
+ public static LeafEffectiveStatement createLeaf(final QName argument, final int flags,
+ final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
+ return new UndeclaredLeafEffectiveStatement(argument, flags, substatements);
+ }
+
public static OutputEffectiveStatement createOutput(final QName argument, final int flags,
final ImmutableList<? extends EffectiveStatement<?, ?>> substatements)
throws SubstatementIndexingException {
--- /dev/null
+/*
+ * 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<QName, LeafStatement>
+ implements LeafEffectiveStatement, LeafSchemaNode, DataSchemaNodeMixin<LeafStatement>,
+ MandatoryMixin<QName, LeafStatement>, MustConstraintMixin<QName, LeafStatement> {
+ 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<? extends @NonNull EffectiveStatement<?, ?>> 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<? extends EffectiveStatement<?, ?>> 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;
+ }
+}
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>rfc8040-model-api</artifactId>
</dependency>
-
-
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-parser-reactor</artifactId>
- <scope>test</scope>
+ <artifactId>yang-repo-api</artifactId>
</dependency>
+
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-parser-rfc7950</artifactId>
+ <artifactId>yang-parser-reactor</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-repo-api</artifactId>
+ <artifactId>yang-parser-rfc7950</artifactId>
<scope>test</scope>
</dependency>
<dependency>
--- /dev/null
+/*
+ * 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<Prerequisite<? extends StmtContext<?, ?, ?>>> prereqs;
+ private final Mutable<?, ?, ?> operations;
+
+ private OperationsCreateLeafStatements(final Mutable<?, ?, ?> operations,
+ final List<Prerequisite<? extends StmtContext<?, ?, ?>>> 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<Prerequisite<? extends StmtContext<?, ?, ?>>>();
+ // 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<? extends Prerequisite<?>> failed) {
+ // We do not really need to fail, as this means reactor will fail anyway
+ }
+
+ private <X, Y extends DeclaredStatement<X>, Z extends EffectiveStatement<X, Y>>
+ StatementSupport<X, Y, Z> getSupport(final StatementDefinition def, final Class<Z> 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<X, Y, Z>) tmp;
+ return ret;
+ }
+}
--- /dev/null
+/*
+ * 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<? extends Mutable<?, ?, ?>> prereq;
+
+ private OperationsValidateModuleAction(final Prerequisite<? extends Mutable<?, ?, ?>> 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<? extends Prerequisite<?>> failed) {
+ // We do not really need to fail, as this means reactor will fail anyway
+ }
+}
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;
@Override
public void onFullDefinitionDeclared(final Mutable<String, YangDataStatement, YangDataEffectiveStatement> 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
--- /dev/null
+/*
+ * 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);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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());
+ }
+}
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;
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");
"/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);
@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);
@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);
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);
@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));
@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);
- }
- }
}
--- /dev/null
+module foo {
+ namespace foo;
+ prefix foo;
+
+ rpc foo;
+}
@Override
public void addEffectiveSubstatement(final Mutable<?, ?, ?> substatement) {
substatements = addEffectiveSubstatement(ensureEffectiveSubstatements(), substatement);
+ afterAddEffectiveSubstatement(substatement);
}
@Override
@Override
public final void addEffectiveSubstatement(final Mutable<?, ?, ?> substatement) {
effective = addEffectiveSubstatement(effective, substatement);
+ afterAddEffectiveSubstatement(substatement);
}
@Override
@Override
@Deprecated
public <X, Y extends DeclaredStatement<X>, Z extends EffectiveStatement<X, Y>>
- Mutable<X, Y, Z> addUndeclaredSubstatement(final StatementSupport<X, Y, Z> support, final X arg) {
+ Mutable<X, Y, Z> createUndeclaredSubstatement(final StatementSupport<X, Y, Z> support, final X arg) {
throw new UnsupportedOperationException();
}
@Override
public final <X, Y extends DeclaredStatement<X>, Z extends EffectiveStatement<X, Y>>
- Mutable<X, Y, Z> addUndeclaredSubstatement(final StatementSupport<X, Y, Z> support, final X arg) {
+ Mutable<X, Y, Z> createUndeclaredSubstatement(final StatementSupport<X, Y, Z> 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;
}
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 <X, Y extends DeclaredStatement<X>, Z extends EffectiveStatement<X, Y>> void finishDeclaration(
+ final UndeclaredStmtCtx<X, Y, Z> substatement) {
+ substatement.definition().onDeclarationFinished(substatement, ModelProcessingPhase.FULL_DECLARATION);
+ }
+
@Override
public final void addEffectiveSubstatements(final Collection<? extends Mutable<?, ?, ?>> statements) {
if (!statements.isEmpty()) {
private static void appendImplicitSubstatement(final Mutable<QName, ActionStatement, ActionEffectiveStatement> stmt,
final QName substatementName) {
- stmt.addUndeclaredSubstatement(
- verifyNotNull(stmt.getFromNamespace(StatementSupportNamespace.class, substatementName)), null);
+ stmt.addEffectiveSubstatement(stmt.createUndeclaredSubstatement(
+ verifyNotNull(stmt.getFromNamespace(StatementSupportNamespace.class, substatementName)), null));
}
}
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;
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;
import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
public final class LeafStatementSupport
- extends AbstractSchemaTreeStatementSupport<LeafStatement, LeafEffectiveStatement> {
+ extends AbstractImplicitStatementSupport<LeafStatement, LeafEffectiveStatement> {
private static final SubstatementValidator SUBSTATEMENT_VALIDATOR =
SubstatementValidator.builder(YangStmtMapping.LEAF)
.addOptional(YangStmtMapping.CONFIG)
@Override
protected LeafEffectiveStatement createEffective(final Current<QName, LeafStatement> stmt,
final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
+ validateEffective(stmt, substatements);
+ return EffectiveStatements.createLeaf(stmt.declared(), stmt.getArgument(), computeFlags(stmt, substatements),
+ substatements);
+ }
+
+ @Override
+ LeafEffectiveStatement createUndeclaredEffective(final UndeclaredCurrent<QName, LeafStatement> stmt,
+ final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
+ validateEffective(stmt, substatements);
+ return UndeclaredStatements.createLeaf(stmt.getArgument(), computeFlags(stmt, substatements), substatements);
+ }
+
+ private static void validateEffective(final @NonNull BoundStmtCtx<QName> stmt,
+ final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
final TypeEffectiveStatement<?> typeStmt = SourceException.throwIfNull(
findFirstStatement(substatements, TypeEffectiveStatement.class), stmt,
"Leaf is missing a 'type' statement");
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
private static void appendImplicitSubstatement(final Mutable<QName, RpcStatement, RpcEffectiveStatement> stmt,
final QName substatementName) {
- stmt.addUndeclaredSubstatement(
- verifyNotNull(stmt.getFromNamespace(StatementSupportNamespace.class, substatementName)), null);
+ stmt.addEffectiveSubstatement(stmt.createUndeclaredSubstatement(
+ verifyNotNull(stmt.getFromNamespace(StatementSupportNamespace.class, substatementName)), null));
}
}
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;
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;
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<TypeStatement> {
+abstract class AbstractTypeStatementSupport extends AbstractTypeSupport<TypeStatement>
+ implements UndeclaredStatementFactory<QName, TypeStatement, TypeEffectiveStatement<TypeStatement>> {
private static final SubstatementValidator SUBSTATEMENT_VALIDATOR =
SubstatementValidator.builder(YangStmtMapping.TYPE)
.addOptional(YangStmtMapping.BASE)
.addAny(YangStmtMapping.TYPE)
.build();
- private static final ImmutableMap<String, BuiltinEffectiveStatement> STATIC_BUILT_IN_TYPES =
- ImmutableMap.<String, BuiltinEffectiveStatement>builder()
- .put(TypeDefinitions.BINARY.getLocalName(), BuiltinEffectiveStatement.BINARY)
- .put(TypeDefinitions.BOOLEAN.getLocalName(), BuiltinEffectiveStatement.BOOLEAN)
- .put(TypeDefinitions.EMPTY.getLocalName(), BuiltinEffectiveStatement.EMPTY)
+ private static final ImmutableMap<QName, BuiltinEffectiveStatement> STATIC_BUILT_IN_TYPES =
+ ImmutableMap.<QName, BuiltinEffectiveStatement>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<String, StatementSupport<?, ?, ?>> dynamicBuiltInTypes;
final Mutable<QName, TypeStatement, EffectiveStatement<QName, TypeStatement>> 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<StmtContext<?, ?, ?>> typePrereq = typeAction.requiresCtx(stmt, TypeNamespace.class,
typeQName, ModelProcessingPhase.EFFECTIVE_MODEL);
return dynamicBuiltInTypes.get(argument);
}
+ @Override
+ public final TypeEffectiveStatement<TypeStatement> createUndeclaredEffective(
+ final UndeclaredCurrent<QName, TypeStatement> stmt,
+ final Stream<? extends StmtContext<?, ?, ?>> effectiveSubstatements) {
+ final ImmutableList<? extends EffectiveStatement<?, ?>> substatements = buildEffectiveSubstatements(stmt,
+ statementsToBuild(stmt, effectiveSubstatements
+ .filter(StmtContext::isSupportedToBuildEffective)
+ .collect(Collectors.toUnmodifiableList())));
+
+ // First look up the proper base type
+ final TypeEffectiveStatement<TypeStatement> 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<QName> ctx,
final ImmutableList<DeclaredStatement<?>> substatements) {
void addEffectiveSubstatements(Collection<? extends Mutable<?, ?, ?>> 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
<X, Y extends DeclaredStatement<X>, Z extends EffectiveStatement<X, Y>>
- @NonNull Mutable<X, Y, Z> addUndeclaredSubstatement(StatementSupport<X, Y, Z> support, @Nullable X arg);
+ @NonNull Mutable<X, Y, Z> createUndeclaredSubstatement(StatementSupport<X, Y, Z> support, @Nullable X arg);
@Beta
void removeStatementFromEffectiveSubstatements(StatementDefinition statementDef);