Rework BindingRuntimeTypes
[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             .toString();
79     }
80
81     private @NonNull LinkageProgress resolveAsTarget() {
82         // Resolved requirement, if we also have original we end resolution here and now
83         final var original = target.tryOriginal();
84         if (original != null) {
85             augment.setTargetGenerator(original);
86             original.addAugment(augment);
87             return LinkageProgress.DONE;
88         }
89         return LinkageProgress.NONE;
90     }
91
92     private @NonNull LinkageProgress resolveAsChild() {
93         // First try local statements without adjustment
94         var gen = target.findLocalSchemaTreeGenerator(qname);
95         if (gen != null) {
96             return progressTo(gen);
97         }
98
99         // Second try local augments, as those are guaranteed to match namespace exactly
100         final var aug = target.findAugmentForGenerator(qname);
101         if (aug != null) {
102             return moveTo(aug);
103         }
104
105         // Third try local groupings, as those perform their own adjustment
106         final var grp = target.findGroupingForGenerator(qname);
107         if (grp != null) {
108             squashNamespaces.add(qname.getModule());
109             localNamespace = grp.getQName().getModule();
110             return moveTo(grp);
111         }
112
113         // Lastly try local statements adjusted with namespace, if applicable
114         gen = target.findLocalSchemaTreeGenerator(squashNamespaces.contains(qname.getModule())
115             ? qname.bindTo(verifyNotNull(localNamespace)) : qname);
116         if (gen != null) {
117             return progressTo(gen);
118         }
119         return LinkageProgress.NONE;
120     }
121
122     private @NonNull LinkageProgress moveTo(final @NonNull AbstractCompositeGenerator<?, ?> newTarget) {
123         target = newTarget;
124         return tryProgress();
125     }
126
127     private @NonNull LinkageProgress progressTo(final @NonNull AbstractExplicitGenerator<?, ?> newTarget) {
128         verify(newTarget instanceof AbstractCompositeGenerator, "Unexpected generator %s", newTarget);
129         target = (AbstractCompositeGenerator<?, ?>) newTarget;
130         qname = remaining.hasNext() ? remaining.next() : null;
131         return tryProgress();
132     }
133
134     private @NonNull LinkageProgress tryProgress() {
135         final var progress = resolve();
136         return progress != LinkageProgress.NONE ? progress : LinkageProgress.SOME;
137     }
138 }