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 com.google.common.base.MoreObjects;
15 import java.util.HashSet;
16 import java.util.Iterator;
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.opendaylight.yangtools.concepts.Mutable;
20 import org.opendaylight.yangtools.yang.common.QName;
21 import org.opendaylight.yangtools.yang.common.QNameModule;
24 * Class tracking state of resolution of an {@code augment} statement's target generator.
27 * This is not quite straightforward. 'path' works on top of schema tree, which is instantiated view. Since we
28 * do not generate duplicate instantiations along 'uses' path, findSchemaTreeGenerator() would satisfy our
29 * request by returning a child of the source 'grouping'.
32 * When that happens, our subsequent lookups need to adjust the namespace being looked up to the grouping's
33 * namespace... except for the case when the step is actually an augmentation, in which case we must not make
37 * Hence we deal with this lookup recursively, dropping namespace hints when we cross into groupings. Note we
38 * take an initial hint -- which UsesAugmentGenerator provides, but ModuleAugmentGenerator does not -- and that
39 * accounts for the difference.
41 final class AugmentRequirement implements Mutable {
42 private final @NonNull Set<QNameModule> squashNamespaces = new HashSet<>(4);
43 private final @NonNull AbstractAugmentGenerator augment;
44 private final @NonNull Iterator<QName> remaining;
46 private @NonNull AbstractCompositeGenerator<?, ?> target;
47 private QNameModule localNamespace;
50 private AugmentRequirement(final AbstractAugmentGenerator augment, final AbstractCompositeGenerator<?, ?> target) {
51 this.augment = requireNonNull(augment);
52 this.target = requireNonNull(target);
53 remaining = augment.statement().argument().getNodeIdentifiers().iterator();
54 qname = remaining.next();
57 AugmentRequirement(final ModuleAugmentGenerator augment, final ModuleGenerator module) {
58 this((AbstractAugmentGenerator) augment, module);
61 AugmentRequirement(final UsesAugmentGenerator augment, final GroupingGenerator grouping) {
62 this((AbstractAugmentGenerator) augment, grouping);
63 // Starting in a grouping: squash namespace references to the grouping's namespace
64 localNamespace = grouping.getQName().getModule();
65 squashNamespaces.add(qname.getModule());
68 @NonNull LinkageProgress resolve() {
69 return qname == null ? resolveAsTarget() : resolveAsChild();
73 public String toString() {
74 return MoreObjects.toStringHelper(this).omitNullValues()
75 .add("augment", augment)
76 .add("target", target)
78 .add("squash", squashNamespaces)
79 .add("local", localNamespace)
83 private @NonNull LinkageProgress resolveAsTarget() {
84 // Resolved requirement, if we also have original we end resolution here and now
85 final var original = target.tryOriginal();
86 if (original != null) {
87 augment.setTargetGenerator(original);
88 original.addAugment(augment);
89 return LinkageProgress.DONE;
91 return LinkageProgress.NONE;
94 private @NonNull LinkageProgress resolveAsChild() {
95 // First try local statements without adjustment
96 var gen = target.findLocalSchemaTreeGenerator(qname);
98 return progressTo(gen);
101 // Second try local augments, as those are guaranteed to match namespace exactly
102 final var aug = target.findAugmentForGenerator(qname);
107 // Third try local groupings, as those perform their own adjustment
108 final var grp = target.findGroupingForGenerator(qname);
110 squashNamespaces.add(qname.getModule());
111 localNamespace = grp.getQName().getModule();
115 // Lastly try local statements adjusted with namespace, if applicable
116 gen = target.findLocalSchemaTreeGenerator(squashNamespaces.contains(qname.getModule())
117 ? qname.bindTo(verifyNotNull(localNamespace)) : qname);
119 return progressTo(gen);
121 return LinkageProgress.NONE;
124 private @NonNull LinkageProgress moveTo(final @NonNull AbstractCompositeGenerator<?, ?> newTarget) {
126 return tryProgress();
129 private @NonNull LinkageProgress progressTo(final @NonNull AbstractExplicitGenerator<?, ?> newTarget) {
130 verify(newTarget instanceof AbstractCompositeGenerator, "Unexpected generator %s", newTarget);
131 target = (AbstractCompositeGenerator<?, ?>) newTarget;
132 qname = remaining.hasNext() ? remaining.next() : null;
133 return tryProgress();
136 private @NonNull LinkageProgress tryProgress() {
137 final var progress = resolve();
138 return progress != LinkageProgress.NONE ? progress : LinkageProgress.SOME;