import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilder;
import org.opendaylight.mdsal.binding.model.ri.BindingTypes;
import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.model.api.AddedByUsesAware;
import org.opendaylight.yangtools.yang.model.api.CopyableNode;
import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.NotificationEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.OutputEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.RpcEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.UsesEffectiveStatement;
}
final void linkUsesDependencies(final GeneratorContext context) {
- // We are establishing two linkages here:
- // - we are resolving 'uses' statements to their corresponding 'grouping' definitions
- // - we propagate those groupings as anchors to any augment statements
+ // We are resolving 'uses' statements to their corresponding 'grouping' definitions
final List<GroupingGenerator> tmp = new ArrayList<>();
for (EffectiveStatement<?, ?> stmt : statement().effectiveSubstatements()) {
if (stmt instanceof UsesEffectiveStatement) {
- final UsesEffectiveStatement uses = (UsesEffectiveStatement) stmt;
- final GroupingGenerator grouping = context.resolveTreeScoped(GroupingGenerator.class, uses.argument());
- tmp.add(grouping);
-
- for (Generator gen : this) {
- if (gen instanceof UsesAugmentGenerator) {
- ((UsesAugmentGenerator) gen).linkGroupingDependency(uses, grouping);
- }
- }
+ tmp.add(context.resolveTreeScoped(GroupingGenerator.class, ((UsesEffectiveStatement) stmt).argument()));
}
}
groupings = List.copyOf(tmp);
}
@Override
- final @Nullable AbstractExplicitGenerator<?> findSchemaTreeGenerator(final QName qname) {
+ final AbstractExplicitGenerator<?> findSchemaTreeGenerator(final QName qname) {
final AbstractExplicitGenerator<?> found = super.findSchemaTreeGenerator(qname);
return found != null ? found : findInferredGenerator(qname);
}
return null;
}
+ final @NonNull AbstractExplicitGenerator<?> resolveSchemaNode(final SchemaNodeIdentifier path) {
+ // This is not quite straightforward. 'path' works on top of schema tree, which is instantiated view. Since we
+ // do not generate duplicate instantiations along 'uses' path, findSchemaTreeGenerator() would satisfy our
+ // request by returning a child of the source 'grouping'.
+ //
+ // When that happens, our subsequent lookups need to adjust the namespace being looked up to the grouping's
+ // namespace... except for the case when the step is actually an augmentation, in which case we must not make
+ // that adjustment.
+ //
+ // Hence we deal with this lookup recursively, dropping namespace hints when we cross into groupings.
+ return resolveSchemaNode(path.getNodeIdentifiers().iterator(), null);
+ }
+
+ private @NonNull AbstractExplicitGenerator<?> resolveSchemaNode(final Iterator<QName> qnames,
+ final @Nullable QNameModule localNamespace) {
+ final QName qname = qnames.next();
+
+ // First try local augments, as those are guaranteed to match namespace exactly
+ for (AbstractAugmentGenerator augment : augments) {
+ final AbstractExplicitGenerator<?> gen = augment.findSchemaTreeGenerator(qname);
+ if (gen != null) {
+ return resolveNext(gen, qnames, null);
+ }
+ }
+
+ // Second try local groupings, as those perform their own adjustment
+ for (GroupingGenerator grouping : groupings) {
+ final QNameModule ns = grouping.statement().argument().getModule();
+ final AbstractExplicitGenerator<?> gen = grouping.findSchemaTreeGenerator(qname.bindTo(ns));
+ if (gen != null) {
+ return resolveNext(gen, qnames, ns);
+ }
+ }
+
+ // Lastly try local statements adjusted with namespace, if applicable
+ final QName lookup = localNamespace == null ? qname : qname.bindTo(localNamespace);
+ final AbstractExplicitGenerator<?> gen = verifyNotNull(super.findSchemaTreeGenerator(lookup),
+ "Failed to find %s as %s in %s", qname, lookup, this);
+ return resolveNext(gen, qnames, localNamespace);
+ }
+
+ private static @NonNull AbstractExplicitGenerator<?> resolveNext(final @NonNull AbstractExplicitGenerator<?> gen,
+ final Iterator<QName> qnames, final QNameModule localNamespace) {
+ if (qnames.hasNext()) {
+ verify(gen instanceof AbstractCompositeGenerator, "Unexpected generator %s", gen);
+ return ((AbstractCompositeGenerator<?>) gen).resolveSchemaNode(qnames, localNamespace);
+ }
+ return gen;
+ }
+
/**
* Update the specified builder to implement interfaces generated for the {@code grouping} statements this generator
* is using.
final UsesEffectiveStatement uses = (UsesEffectiveStatement) stmt;
for (EffectiveStatement<?, ?> usesSub : uses.effectiveSubstatements()) {
if (usesSub instanceof AugmentEffectiveStatement) {
- tmpAug.add(new UsesAugmentGenerator((AugmentEffectiveStatement) usesSub, this, uses));
+ tmpAug.add(new UsesAugmentGenerator((AugmentEffectiveStatement) usesSub, this));
}
}
} else {
package org.opendaylight.mdsal.binding.generator.impl.reactor;
import static com.google.common.base.Verify.verify;
-import static com.google.common.base.Verify.verifyNotNull;
import static java.util.Objects.requireNonNull;
import com.google.common.base.MoreObjects.ToStringHelper;
import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
import org.opendaylight.yangtools.yang.common.AbstractQName;
import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.model.api.AddedByUsesAware;
import org.opendaylight.yangtools.yang.model.api.CopyableNode;
import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionEffectiveStatement;
-import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
return null;
}
- final @NonNull AbstractExplicitGenerator<?> resolveSchemaNode(final @NonNull SchemaNodeIdentifier path,
- final @Nullable QNameModule targetModule) {
- AbstractExplicitGenerator<?> current = this;
- QNameModule currentModule = targetModule;
-
- for (QName next : path.getNodeIdentifiers()) {
- final QName qname = currentModule == null ? next : next.bindTo(currentModule);
- current = verifyNotNull(current.findSchemaTreeGenerator(qname),
- "Failed to find %s as %s in %s", next, qname, current);
-
- final QNameModule foundNamespace = current.getQName().getModule();
- if (!foundNamespace.equals(qname.getModule())) {
- // We have located a different QName than the one we were looking for. We need to make sure we adjust
- // all subsequent QNames to this new namespace
- currentModule = foundNamespace;
- }
- }
-
- return current;
- }
-
final @NonNull QName getQName() {
final Object arg = statement.argument();
verify(arg instanceof QName, "Unexpected argument %s", arg);
import edu.umd.cs.findbugs.annotations.Nullable;
import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.model.api.PathExpression;
import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
-import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
/**
* Abstract view on generation tree as viewed by a particular {@link Generator}.
abstract <E extends EffectiveStatement<QName, ?>, G extends AbstractExplicitGenerator<E>>
@NonNull G resolveTreeScoped(@NonNull Class<G> type, @NonNull QName argument);
- abstract @NonNull AbstractExplicitGenerator<?> resolveSchemaNode(@NonNull SchemaNodeIdentifier path);
+ abstract @NonNull ModuleGenerator resolveModule(@NonNull QNameModule namespace);
- abstract @NonNull IdentityGenerator resolveIdentity(@NonNull QName name);
+ final @NonNull IdentityGenerator resolveIdentity(final @NonNull QName name) {
+ for (Generator gen : resolveModule(name.getModule())) {
+ if (gen instanceof IdentityGenerator) {
+ final IdentityGenerator idgen = (IdentityGenerator) gen;
+ if (name.equals(idgen.statement().argument())) {
+ return idgen;
+ }
+ }
+ }
+ throw new IllegalStateException("Failed to find identity " + name);
+ }
final @NonNull TypedefGenerator resolveTypedef(final @NonNull QName qname) {
return resolveTreeScoped(TypedefGenerator.class, qname);
*/
package org.opendaylight.mdsal.binding.generator.impl.reactor;
+import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Verify.verify;
import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
import com.google.common.base.Stopwatch;
import com.google.common.base.VerifyException;
import org.opendaylight.yangtools.yang.model.api.PathExpression;
import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
-import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
import org.opendaylight.yangtools.yang.model.ri.type.TypeBuilder;
import org.opendaylight.yangtools.yang.model.spi.ModuleDependencySort;
import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
}
}
- @Override
- AbstractExplicitGenerator<?> resolveSchemaNode(final SchemaNodeIdentifier path) {
- verify(path instanceof SchemaNodeIdentifier.Absolute, "Unexpected path %s", path);
- return verifyNotNull(generators.get(path.firstNodeIdentifier().getModule()), "Cannot find module for %s", path)
- .resolveSchemaNode(path, null);
- }
-
@Override
<E extends EffectiveStatement<QName, ?>, G extends AbstractExplicitGenerator<E>> G resolveTreeScoped(
final Class<G> type, final QName argument) {
}
@Override
- IdentityGenerator resolveIdentity(final QName name) {
- final ModuleGenerator module = generators.get(name.getModule());
- if (module != null) {
- for (Generator gen : module) {
- if (gen instanceof IdentityGenerator) {
- final IdentityGenerator idgen = (IdentityGenerator) gen;
- if (name.equals(idgen.statement().argument())) {
- return idgen;
- }
- }
- }
- }
- throw new IllegalStateException("Failed to find identity " + name);
+ ModuleGenerator resolveModule(final QNameModule namespace) {
+ final ModuleGenerator module = generators.get(requireNonNull(namespace));
+ checkState(module != null, "Failed to find module for %s", namespace);
+ return module;
}
@Override
package org.opendaylight.mdsal.binding.generator.impl.reactor;
import org.opendaylight.yangtools.yang.model.api.stmt.AugmentEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
/**
* Generator corresponding to a {@code augment} statement used as a child of a {@code module} statement.
}
void linkAugmentationTarget(final GeneratorContext context) {
- // FIXME: do we need two-step resolution here? we probably have solved this somehow, or it's part of...
- // FIXME: MDSAL-696: this looks like the sort of check which should be involved in replacing getOriginal()
- //
- // if (targetSchemaNode instanceof DataSchemaNode && ((DataSchemaNode) targetSchemaNode).isAddedByUses()) {
- // if (targetSchemaNode instanceof DerivableSchemaNode) {
- // targetSchemaNode = ((DerivableSchemaNode) targetSchemaNode).getOriginal().orElse(null);
- // }
- // if (targetSchemaNode == null) {
- // throw new IllegalStateException("Failed to find target node from grouping in augmentation "
- // + augSchema + " in module " + context.module().getName());
- // }
- // }
-
- setTargetGenerator(context.resolveSchemaNode(statement().argument()));
+ final SchemaNodeIdentifier path = statement().argument();
+ final ModuleGenerator module = context.resolveModule(path.firstNodeIdentifier().getModule());
+ setTargetGenerator(module.resolveSchemaNode(path));
}
}
*/
package org.opendaylight.mdsal.binding.generator.impl.reactor;
-import static com.google.common.base.Verify.verify;
-import static com.google.common.base.Verify.verifyNotNull;
-import static java.util.Objects.requireNonNull;
-
import org.opendaylight.yangtools.yang.model.api.stmt.AugmentEffectiveStatement;
-import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
-import org.opendaylight.yangtools.yang.model.api.stmt.UsesEffectiveStatement;
/**
* Generator corresponding to a {@code augment} statement used as a child of a {@code uses} statement.
*/
final class UsesAugmentGenerator extends AbstractAugmentGenerator {
- private final UsesEffectiveStatement uses;
-
- private GroupingGenerator grouping;
-
- UsesAugmentGenerator(final AugmentEffectiveStatement statement, final AbstractCompositeGenerator<?> parent,
- final UsesEffectiveStatement uses) {
+ UsesAugmentGenerator(final AugmentEffectiveStatement statement, final AbstractCompositeGenerator<?> parent) {
super(statement, parent);
- this.uses = requireNonNull(uses);
- }
-
- void linkGroupingDependency(final UsesEffectiveStatement checkUses, final GroupingGenerator resolvedGrouping) {
- if (uses.equals(checkUses)) {
- verify(grouping == null, "Attempted to relink %s from %s to %s", this, grouping, resolvedGrouping);
- this.grouping = requireNonNull(resolvedGrouping);
- }
}
@Override
void loadTargetGenerator() {
- final GroupingGenerator grp = verifyNotNull(grouping, "No grouping linked in %s", this);
- final SchemaNodeIdentifier path = statement().argument();
-
- /*
- * Here we are going in the opposite direction of RFC7950, section 3.13:
- *
- * The effect of a "uses" reference to a grouping is that the nodes
- * defined by the grouping are copied into the current schema tree and
- * are then updated according to the "refine" and "augment" statements.
- *
- * Our argument is composed of QNames in the current schema tree's namespace, but the grouping may have been
- * defined in a different module -- and therefore it knows those children under its namespace. Adjust the path
- * we are searching if that happens to be the case.
- */
- setTargetGenerator(grp.resolveSchemaNode(path, grp.statement().argument().getModule()));
+ // Here we are going in the opposite direction of RFC7950, section 7.13:
+ //
+ // The effect of a "uses" reference to a grouping is that the nodes
+ // defined by the grouping are copied into the current schema tree and
+ // are then updated according to the "refine" and "augment" statements.
+ //
+ // Our parent here is *not* the uses statement, but rather the statement which contains uses -- and its
+ // getSchemaTreeGenerator() is well equipped to deal with the namespace hopping needed to perform the lookups
+ setTargetGenerator(getParent().resolveSchemaNode(statement().argument()));
}
}
--- /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.mdsal.binding.generator.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class Mdsal715Test {
+ @Test
+ public void testAugmentLinking() {
+ final var generatedTypes = DefaultBindingGenerator.generateFor(
+ YangParserTestUtils.parseYangResourceDirectory("/mdsal715"));
+ assertEquals(12, generatedTypes.size());
+ }
+}
--- /dev/null
+module test-module-1 {
+ namespace "test-module-1";
+ prefix "tm1";
+
+ import test-module-2 {
+ prefix tm2;
+ }
+
+ container test-cont {
+ list test-list {
+ uses tm2:cont-1-grouping;
+ }
+ }
+}
--- /dev/null
+module test-module-2 {
+ namespace "test-module-2";
+ prefix "tm2";
+
+ import test-module-3 {
+ prefix tm3;
+ }
+
+ grouping cont-1-grouping {
+ container used-cont-1 {
+ uses tm3:cont-2-grouping;
+ }
+ }
+}
--- /dev/null
+module test-module-3 {
+ namespace "test-module-3";
+ prefix "tm3";
+
+ grouping cont-2-grouping {
+ container used-cont-2;
+ }
+}
--- /dev/null
+module test-module-4 {
+ namespace "test-module-4";
+ prefix "tm4";
+
+ import test-module-1 {
+ prefix tm1;
+ }
+
+ augment "/tm1:test-cont/tm1:test-list/tm1:used-cont-1/tm1:used-cont-2" {
+ container augmented-cont-1 {
+ list augmented-list;
+ }
+ }
+
+ augment "/tm1:test-cont/tm1:test-list/tm1:used-cont-1/tm1:used-cont-2/tm4:augmented-cont-1/tm4:augmented-list" {
+ container augmented-cont-2;
+ }
+}