9a59a88f1459c671ec8d6e8e4773b9050b4b320b
[mdsal.git] / binding / mdsal-binding-generator / src / main / java / org / opendaylight / mdsal / binding / generator / impl / reactor / AbstractExplicitGenerator.java
1 /*
2  * Copyright (c) 2021 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.ToStringHelper;
15 import org.eclipse.jdt.annotation.NonNull;
16 import org.eclipse.jdt.annotation.Nullable;
17 import org.opendaylight.mdsal.binding.generator.impl.reactor.CollisionDomain.Member;
18 import org.opendaylight.mdsal.binding.model.api.MethodSignature.ValueMechanics;
19 import org.opendaylight.mdsal.binding.model.api.Type;
20 import org.opendaylight.mdsal.binding.model.api.TypeMemberComment;
21 import org.opendaylight.mdsal.binding.model.api.type.builder.AnnotableTypeBuilder;
22 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilderBase;
23 import org.opendaylight.mdsal.binding.model.api.type.builder.MethodSignatureBuilder;
24 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
25 import org.opendaylight.yangtools.yang.common.AbstractQName;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.common.QNameModule;
28 import org.opendaylight.yangtools.yang.model.api.AddedByUsesAware;
29 import org.opendaylight.yangtools.yang.model.api.CopyableNode;
30 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
31 import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionEffectiveStatement;
32 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
33 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 /**
38  * An explicit {@link Generator}, associated with a particular {@link EffectiveStatement}.
39  */
40 public abstract class AbstractExplicitGenerator<T extends EffectiveStatement<?, ?>> extends Generator
41         implements CopyableNode {
42     private static final Logger LOG = LoggerFactory.getLogger(AbstractExplicitGenerator.class);
43
44     private final @NonNull T statement;
45
46     // FIXME: this, along with AbstractTypeObjectGenerator's (and TypedefGenerator's) fields should be better-controlled
47     //        with explicit sequencing guards. It it currently stands, we are expending two (or more) additional fields
48     //        to express downstream linking. If we had the concept of resolution step (an enum), we could just get by
49     //        with a simple queue of Step/Callback pairs, which would trigger as needed. For an example see how
50     //        AbstractTypeObjectGenerator manages baseGen/inferred fields.
51     private AbstractExplicitGenerator<?> prev;
52
53     AbstractExplicitGenerator(final T statement) {
54         this.statement = requireNonNull(statement);
55     }
56
57     AbstractExplicitGenerator(final T statement, final AbstractCompositeGenerator<?> parent) {
58         super(parent);
59         this.statement = requireNonNull(statement);
60     }
61
62     /**
63      * Return the {@link EffectiveStatement} associated with this generator.
64      *
65      * @return An EffectiveStatement
66      */
67     public final @NonNull T statement() {
68         return statement;
69     }
70
71     @Override
72     public final boolean isAddedByUses() {
73         return statement instanceof AddedByUsesAware && ((AddedByUsesAware) statement).isAddedByUses();
74     }
75
76     @Override
77     public final boolean isAugmenting() {
78         return statement instanceof CopyableNode && ((CopyableNode) statement).isAugmenting();
79     }
80
81     final void linkOriginalGenerator(final GeneratorContext context) {
82         if (isAddedByUses() || isAugmenting()) {
83             LOG.trace("Linking {}", this);
84             prev = getParent().getOriginalChild(getQName());
85             LOG.trace("Linked {} to {}", this, prev);
86         }
87     }
88
89     final @Nullable AbstractExplicitGenerator<?> previous() {
90         return prev;
91     }
92
93     @NonNull AbstractExplicitGenerator<?> getOriginal() {
94         return prev == null ? this : prev.getOriginal();
95     }
96
97     @Nullable AbstractExplicitGenerator<?> findSchemaTreeGenerator(final QName qname) {
98         for (Generator child : this) {
99             if (child instanceof AbstractExplicitGenerator) {
100                 final AbstractExplicitGenerator<?> gen = (AbstractExplicitGenerator<?>) child;
101                 final EffectiveStatement<?, ?> stmt = gen.statement();
102                 if (stmt instanceof SchemaTreeEffectiveStatement && qname.equals(stmt.argument())) {
103                     return gen;
104                 }
105             }
106         }
107         return null;
108     }
109
110     final @NonNull AbstractExplicitGenerator<?> resolveSchemaNode(final @NonNull SchemaNodeIdentifier path,
111             final @Nullable QNameModule targetModule) {
112         AbstractExplicitGenerator<?> current = this;
113         QNameModule currentModule = targetModule;
114
115         for (QName next : path.getNodeIdentifiers()) {
116             final QName qname = currentModule == null ? next : next.bindTo(currentModule);
117             current = verifyNotNull(current.findSchemaTreeGenerator(qname),
118                 "Failed to find %s as %s in %s", next, qname, current);
119
120             final QNameModule foundNamespace = current.getQName().getModule();
121             if (!foundNamespace.equals(qname.getModule())) {
122                 // We have located a different QName than the one we were looking for. We need to make sure we adjust
123                 // all subsequent QNames to this new namespace
124                 currentModule = foundNamespace;
125             }
126         }
127
128         return current;
129     }
130
131     final @NonNull QName getQName() {
132         final Object arg = statement.argument();
133         verify(arg instanceof QName, "Unexpected argument %s", arg);
134         return (QName) arg;
135     }
136
137     @NonNull AbstractQName localName() {
138         // FIXME: this should be done in a nicer way
139         final Object argument = statement.argument();
140         verify(argument instanceof AbstractQName, "Illegal argument %s", argument);
141         return (AbstractQName) argument;
142     }
143
144     @Override
145     ClassPlacement classPlacement() {
146         // We process nodes introduced through augment or uses separately
147         // FIXME: this is not quite right!
148         return isAddedByUses() || isAugmenting() ? ClassPlacement.NONE : ClassPlacement.TOP_LEVEL;
149     }
150
151     @Override
152     Member createMember(final CollisionDomain domain) {
153         return domain.addPrimary(this, new CamelCaseNamingStrategy(namespace(), localName()));
154     }
155
156     void addAsGetterMethod(final @NonNull GeneratedTypeBuilderBase<?> builder,
157             final @NonNull TypeBuilderFactory builderFactory) {
158         if (isAugmenting()) {
159             // Do not process augmented nodes: they will be taken care of in their home augmentation
160             return;
161         }
162         if (isAddedByUses()) {
163             // If this generator has been added by a uses node, it is already taken care of by the corresponding
164             // grouping. There is one exception to this rule: 'type leafref' can use a relative path to point
165             // outside of its home grouping. In this case we need to examine the instantiation until we succeed in
166             // resolving the reference.
167             addAsGetterMethodOverride(builder, builderFactory);
168             return;
169         }
170
171         final Type returnType = methodReturnType(builderFactory);
172         constructGetter(builder, returnType);
173         constructRequire(builder, returnType);
174     }
175
176     MethodSignatureBuilder constructGetter(final GeneratedTypeBuilderBase<?> builder, final Type returnType) {
177         return constructGetter(builder, returnType, BindingMapping.getGetterMethodName(localName().getLocalName()));
178     }
179
180     final MethodSignatureBuilder constructGetter(final GeneratedTypeBuilderBase<?> builder,
181             final Type returnType, final String methodName) {
182         final MethodSignatureBuilder getMethod = builder.addMethod(methodName).setReturnType(returnType);
183
184         annotateDeprecatedIfNecessary(getMethod);
185
186         statement.findFirstEffectiveSubstatementArgument(DescriptionEffectiveStatement.class)
187             .map(TypeMemberComment::referenceOf).ifPresent(getMethod::setComment);
188
189         return getMethod;
190     }
191
192     void constructRequire(final GeneratedTypeBuilderBase<?> builder, final Type returnType) {
193         // No-op in most cases
194     }
195
196     final void constructRequireImpl(final GeneratedTypeBuilderBase<?> builder, final Type returnType) {
197         constructGetter(builder, returnType, BindingMapping.getRequireMethodName(localName().getLocalName()))
198             .setDefault(true)
199             .setMechanics(ValueMechanics.NONNULL);
200     }
201
202     void addAsGetterMethodOverride(final @NonNull GeneratedTypeBuilderBase<?> builder,
203             final @NonNull TypeBuilderFactory builderFactory) {
204         // No-op for most cases
205     }
206
207     @NonNull Type methodReturnType(final @NonNull TypeBuilderFactory builderFactory) {
208         return getGeneratedType(builderFactory);
209     }
210
211     final void annotateDeprecatedIfNecessary(final AnnotableTypeBuilder builder) {
212         annotateDeprecatedIfNecessary(statement, builder);
213     }
214
215     @Override
216     ToStringHelper addToStringAttributes(final ToStringHelper helper) {
217         helper.add("argument", statement.argument());
218
219         if (isAddedByUses()) {
220             helper.addValue("addedByUses");
221         }
222         if (isAugmenting()) {
223             helper.addValue("augmenting");
224         }
225         return helper;
226     }
227 }