Populate ietf-restconf operations container 89/97689/35
authorRobert Varga <robert.varga@pantheon.tech>
Thu, 30 Sep 2021 17:30:07 +0000 (19:30 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Tue, 4 Jan 2022 13:11:19 +0000 (14:11 +0100)
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 <robert.varga@pantheon.tech>
20 files changed:
model/yang-model-ri/src/main/java/org/opendaylight/yangtools/yang/model/ri/stmt/EffectiveStatements.java
model/yang-model-ri/src/main/java/org/opendaylight/yangtools/yang/model/ri/stmt/UndeclaredStatements.java
model/yang-model-ri/src/main/java/org/opendaylight/yangtools/yang/model/ri/stmt/impl/eff/UndeclaredLeafEffectiveStatement.java [new file with mode: 0644]
parser/rfc8040-parser-support/pom.xml
parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/OperationsCreateLeafStatements.java [new file with mode: 0644]
parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/OperationsValidateModuleAction.java [new file with mode: 0644]
parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/YangDataStatementSupport.java
parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/AbstractYangDataTest.java [new file with mode: 0644]
parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/YT1338Test.java [new file with mode: 0644]
parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/YangDataExtensionTest.java
parser/rfc8040-parser-support/src/test/resources/yt1338/foo.yang [new file with mode: 0644]
parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/InferredStatementContext.java
parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/OriginalStmtCtx.java
parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ReplicaStatementContext.java
parser/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java
parser/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/meta/ActionStatementSupport.java
parser/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/meta/LeafStatementSupport.java
parser/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/meta/RpcStatementSupport.java
parser/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/AbstractTypeStatementSupport.java
parser/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StmtContext.java

index 0aac30f91b4c5b90debe6052bb0a1e754e16ebe7..dc6590032b8778d3fe45d86b9d4a22746fc5033b 100644 (file)
@@ -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,
index 71ce1f91b9b842a74d63a5695e32bb066f8992c5..67a9625ef70ee06c3a405e359ac892d42c1778c0 100644 (file)
@@ -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<? 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 {
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 (file)
index 0000000..adc6a6f
--- /dev/null
@@ -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<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;
+    }
+}
index 531b609fb8347ae838031cef786cf316e24d6764..4076bb132892c7ad217494ee4e4d3f8dae762386 100644 (file)
             <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>
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 (file)
index 0000000..8703cbd
--- /dev/null
@@ -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<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;
+    }
+}
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 (file)
index 0000000..80ae818
--- /dev/null
@@ -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<? 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
+    }
+}
index f746ca1c69fbc3b67224100903bfd44ebfdef8db..6c18978c6cf8ca86666ff5ddd662e6e1013a6d54 100644 (file)
@@ -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<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
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 (file)
index 0000000..5a67d59
--- /dev/null
@@ -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 (file)
index 0000000..9375412
--- /dev/null
@@ -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());
+    }
+}
index ef8beeabdd7e0a38077f2d0524e3ed9e9067e253..f06eb4cf757329fe6e8c8f859a5701fe57c1f6c2 100644 (file)
@@ -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 (file)
index 0000000..669998c
--- /dev/null
@@ -0,0 +1,6 @@
+module foo {
+  namespace foo;
+  prefix foo;
+
+  rpc foo;
+}
index c0091937abccf97bbdb6151564a10ad048c2fe28..b1693ae1d592d3090b0a45bcca01f4420f24506b 100644 (file)
@@ -194,6 +194,7 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
     @Override
     public void addEffectiveSubstatement(final Mutable<?, ?, ?> substatement) {
         substatements = addEffectiveSubstatement(ensureEffectiveSubstatements(), substatement);
+        afterAddEffectiveSubstatement(substatement);
     }
 
     @Override
index c4c7d95c8889dab9360b0ad6afbf811457a822cd..7492ddd2f79f41a82d3a79205081b39ee7ded3c8 100644 (file)
@@ -86,6 +86,7 @@ abstract class OriginalStmtCtx<A, D extends DeclaredStatement<A>, E extends Effe
     @Override
     public final void addEffectiveSubstatement(final Mutable<?, ?, ?> substatement) {
         effective = addEffectiveSubstatement(effective, substatement);
+        afterAddEffectiveSubstatement(substatement);
     }
 
     @Override
index f74451c17cebf2aa9e87ed11564194ef679dac7e..58434926184c1d0a83ca160e61df216378c3f7b7 100644 (file)
@@ -187,7 +187,7 @@ final class ReplicaStatementContext<A, D extends DeclaredStatement<A>, E extends
     @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();
     }
 
index c7bd79a01adc534bd35adac7fbf69b5c53ca7428..438c6e6365d87ad367c189b72323bcb47ccdfbf8 100644 (file)
@@ -331,13 +331,12 @@ abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E extends
 
     @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;
     }
 
@@ -352,6 +351,19 @@ abstract class StatementContextBase<A, D extends DeclaredStatement<A>, 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 <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()) {
index 1d2d5c15e81ffeea36a641e099af1928c087521e..ca8e7426257d1bd9bd61c6c8c0fdf217f182b040 100644 (file)
@@ -132,7 +132,7 @@ public final class ActionStatementSupport extends
 
     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));
     }
 }
index 037195e2bc44ede5df1460a28cab6cb825e4df6e..ff7073f91a2a4aac4ac87be201d040004cbd62ff 100644 (file)
@@ -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<LeafStatement, LeafEffectiveStatement> {
+        extends AbstractImplicitStatementSupport<LeafStatement, LeafEffectiveStatement> {
     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<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");
@@ -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
index b696325e4d94a207d066c3db597a46719c3baae5..a228e35c6d87f06291a87c617cc49f2e13103d33 100644 (file)
@@ -100,7 +100,7 @@ public final class RpcStatementSupport extends AbstractSchemaTreeStatementSuppor
 
     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));
     }
 }
index 086f8d9f094322cf023999a36766b3f3e02f45d4..aea249a6ac74de6a985c5aa13f58c3da4661b016 100644 (file)
@@ -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<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)
@@ -99,23 +104,23 @@ abstract class AbstractTypeStatementSupport extends AbstractTypeSupport<TypeStat
             .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;
@@ -138,13 +143,13 @@ abstract class AbstractTypeStatementSupport extends AbstractTypeSupport<TypeStat
             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);
@@ -178,6 +183,25 @@ abstract class AbstractTypeStatementSupport extends AbstractTypeSupport<TypeStat
         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) {
index 7d0528a9522291b350dcb6b1cbaf406c981216b9..d940675907e05ad0d2076eaa008929102d3106d3 100644 (file)
@@ -310,21 +310,25 @@ public interface StmtContext<A, D extends DeclaredStatement<A>, E extends Effect
         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);