Include namespace manipulations in error report
[mdsal.git] / binding / mdsal-binding-generator / src / main / java / org / opendaylight / mdsal / binding / generator / impl / reactor / AugmentRequirement.java
1 /*
2  * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.mdsal.binding.generator.impl.reactor;
9
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;
13
14 import com.google.common.base.MoreObjects;
15 import java.util.HashSet;
16 import java.util.Iterator;
17 import java.util.Set;
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;
22
23 /**
24  * Class tracking state of resolution of an {@code augment} statement's target generator.
25  *
26  * <p>
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'.
30  *
31  * <p>
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
34  * that adjustment.
35  *
36  * <p>
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.
40  */
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;
45
46     private @NonNull AbstractCompositeGenerator<?, ?> target;
47     private QNameModule localNamespace;
48     private QName qname;
49
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();
55     }
56
57     AugmentRequirement(final ModuleAugmentGenerator augment, final ModuleGenerator module) {
58         this((AbstractAugmentGenerator) augment, module);
59     }
60
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());
66     }
67
68     @NonNull LinkageProgress resolve() {
69         return qname == null ? resolveAsTarget() : resolveAsChild();
70     }
71
72     @Override
73     public String toString() {
74         return MoreObjects.toStringHelper(this).omitNullValues()
75             .add("augment", augment)
76             .add("target", target)
77             .add("qname", qname)
78             .add("squash", squashNamespaces)
79             .add("local", localNamespace)
80             .toString();
81     }
82
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;
90         }
91         return LinkageProgress.NONE;
92     }
93
94     private @NonNull LinkageProgress resolveAsChild() {
95         // First try local statements without adjustment
96         var gen = target.findLocalSchemaTreeGenerator(qname);
97         if (gen != null) {
98             return progressTo(gen);
99         }
100
101         // Second try local augments, as those are guaranteed to match namespace exactly
102         final var aug = target.findAugmentForGenerator(qname);
103         if (aug != null) {
104             return moveTo(aug);
105         }
106
107         // Third try local groupings, as those perform their own adjustment
108         final var grp = target.findGroupingForGenerator(qname);
109         if (grp != null) {
110             squashNamespaces.add(qname.getModule());
111             localNamespace = grp.getQName().getModule();
112             return moveTo(grp);
113         }
114
115         // Lastly try local statements adjusted with namespace, if applicable
116         gen = target.findLocalSchemaTreeGenerator(squashNamespaces.contains(qname.getModule())
117             ? qname.bindTo(verifyNotNull(localNamespace)) : qname);
118         if (gen != null) {
119             return progressTo(gen);
120         }
121         return LinkageProgress.NONE;
122     }
123
124     private @NonNull LinkageProgress moveTo(final @NonNull AbstractCompositeGenerator<?, ?> newTarget) {
125         target = newTarget;
126         return tryProgress();
127     }
128
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();
134     }
135
136     private @NonNull LinkageProgress tryProgress() {
137         final var progress = resolve();
138         return progress != LinkageProgress.NONE ? progress : LinkageProgress.SOME;
139     }
140 }