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