2 * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.mdsal.binding.generator.impl.reactor;
10 import static com.google.common.base.Verify.verify;
11 import static com.google.common.base.Verify.verifyNotNull;
12 import static java.util.Objects.requireNonNull;
14 import java.util.HashSet;
15 import java.util.Iterator;
17 import org.eclipse.jdt.annotation.NonNull;
18 import org.opendaylight.yangtools.concepts.Mutable;
19 import org.opendaylight.yangtools.yang.common.QName;
20 import org.opendaylight.yangtools.yang.common.QNameModule;
23 * Class tracking state of resolution of an {@code augment} statement's target generator.
26 * This is not quite straightforward. 'path' works on top of schema tree, which is instantiated view. Since we
27 * do not generate duplicate instantiations along 'uses' path, findSchemaTreeGenerator() would satisfy our
28 * request by returning a child of the source 'grouping'.
31 * When that happens, our subsequent lookups need to adjust the namespace being looked up to the grouping's
32 * namespace... except for the case when the step is actually an augmentation, in which case we must not make
36 * Hence we deal with this lookup recursively, dropping namespace hints when we cross into groupings. Note we
37 * take an initial hint -- which UsesAugmentGenerator provides, but ModuleAugmentGenerator does not -- and that
38 * accounts for the difference.
40 final class AugmentRequirement implements Mutable {
41 private final @NonNull Set<QNameModule> squashNamespaces = new HashSet<>(4);
42 private final @NonNull AbstractAugmentGenerator augment;
43 private final @NonNull Iterator<QName> remaining;
45 private @NonNull AbstractCompositeGenerator<?> target;
46 private QNameModule localNamespace;
49 private AugmentRequirement(final AbstractAugmentGenerator augment, final AbstractCompositeGenerator<?> target) {
50 this.augment = requireNonNull(augment);
51 this.target = requireNonNull(target);
52 remaining = augment.statement().argument().getNodeIdentifiers().iterator();
53 qname = remaining.next();
56 AugmentRequirement(final ModuleAugmentGenerator augment, final ModuleGenerator module) {
57 this((AbstractAugmentGenerator) augment, module);
60 AugmentRequirement(final UsesAugmentGenerator augment, final GroupingGenerator grouping) {
61 this((AbstractAugmentGenerator) augment, grouping);
62 // Starting in a grouping: squash namespace references to the grouping's namespace
63 localNamespace = grouping.getQName().getModule();
64 squashNamespaces.add(qname.getModule());
67 @NonNull LinkageProgress resolve() {
68 return qname == null ? resolveAsTarget() : resolveAsChild();
71 private @NonNull LinkageProgress resolveAsTarget() {
72 // Resolved requirement, if we also have original we end resolution here and now
73 final var original = target.tryOriginal();
74 if (original != null) {
75 augment.setTargetGenerator(original);
76 original.addAugment(augment);
77 return LinkageProgress.DONE;
79 return LinkageProgress.NONE;
82 private @NonNull LinkageProgress resolveAsChild() {
83 // First try local statements without adjustment
84 var gen = target.findLocalSchemaTreeGenerator(qname);
86 return progressTo(gen);
89 // Second try local augments, as those are guaranteed to match namespace exactly
90 final var aug = target.findAugmentForGenerator(qname);
95 // Third try local groupings, as those perform their own adjustment
96 final var grp = target.findGroupingForGenerator(qname);
98 squashNamespaces.add(qname.getModule());
99 localNamespace = grp.getQName().getModule();
103 // Lastly try local statements adjusted with namespace, if applicable
104 gen = target.findLocalSchemaTreeGenerator(squashNamespaces.contains(qname.getModule())
105 ? qname.bindTo(verifyNotNull(localNamespace)) : qname);
107 return progressTo(gen);
109 return LinkageProgress.NONE;
112 private @NonNull LinkageProgress moveTo(final @NonNull AbstractCompositeGenerator<?> newTarget) {
114 return tryProgress();
117 private @NonNull LinkageProgress progressTo(final @NonNull AbstractExplicitGenerator<?> newTarget) {
118 verify(newTarget instanceof AbstractCompositeGenerator, "Unexpected generator %s", newTarget);
119 target = (AbstractCompositeGenerator<?>) newTarget;
120 qname = remaining.hasNext() ? remaining.next() : null;
121 return tryProgress();
124 private @NonNull LinkageProgress tryProgress() {
125 final var progress = resolve();
126 return progress != LinkageProgress.NONE ? progress : LinkageProgress.SOME;