Bump to odlparent-9.0.0/yangtools-7.0.1-SNAPSHOT
[mdsal.git] / binding / mdsal-binding-generator-impl / 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.Type;
19 import org.opendaylight.mdsal.binding.model.api.type.builder.AnnotableTypeBuilder;
20 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilderBase;
21 import org.opendaylight.mdsal.binding.model.api.type.builder.MethodSignatureBuilder;
22 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
23 import org.opendaylight.yangtools.yang.common.AbstractQName;
24 import org.opendaylight.yangtools.yang.common.QName;
25 import org.opendaylight.yangtools.yang.common.QNameModule;
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.SchemaNodeIdentifier;
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(final GeneratorContext context) {
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 AbstractExplicitGenerator<?> resolveSchemaNode(final @NonNull SchemaNodeIdentifier path,
108             final @Nullable QNameModule targetModule) {
109         AbstractExplicitGenerator<?> current = this;
110         QNameModule currentModule = targetModule;
111
112         for (QName next : path.getNodeIdentifiers()) {
113             final QName qname = currentModule == null ? next : next.bindTo(currentModule);
114             current = verifyNotNull(current.findSchemaTreeGenerator(qname),
115                 "Failed to find %s as %s in %s", next, qname, current);
116
117             final QNameModule foundNamespace = current.getQName().getModule();
118             if (!foundNamespace.equals(qname.getModule())) {
119                 // We have located a different QName than the one we were looking for. We need to make sure we adjust
120                 // all subsequent QNames to this new namespace
121                 currentModule = foundNamespace;
122             }
123         }
124
125         return current;
126     }
127
128     final @NonNull QName getQName() {
129         final Object arg = statement.argument();
130         verify(arg instanceof QName, "Unexpected argument %s", arg);
131         return (QName) arg;
132     }
133
134     @NonNull AbstractQName localName() {
135         // FIXME: this should be done in a nicer way
136         final Object argument = statement.argument();
137         verify(argument instanceof AbstractQName, "Illegal argument %s", argument);
138         return (AbstractQName) argument;
139     }
140
141     @Override
142     ClassPlacement classPlacement() {
143         // We process nodes introduced through augment or uses separately
144         // FIXME: this is not quite right!
145         return isAddedByUses() || isAugmenting() ? ClassPlacement.NONE : ClassPlacement.TOP_LEVEL;
146     }
147
148     @Override
149     Member createMember(final CollisionDomain domain) {
150         return domain.addPrimary(new CamelCaseNamingStrategy(namespace(), localName()));
151     }
152
153     void addAsGetterMethod(final @NonNull GeneratedTypeBuilderBase<?> builder,
154             final @NonNull TypeBuilderFactory builderFactory) {
155         if (isAugmenting()) {
156             // Do not process augmented nodes: they will be taken care of in their home augmentation
157             return;
158         }
159         if (isAddedByUses()) {
160             // If this generator has been added by a uses node, it is already taken care of by the corresponding
161             // grouping. There is one exception to this rule: 'type leafref' can use a relative path to point
162             // outside of its home grouping. In this case we need to examine the instantiation until we succeed in
163             // resolving the reference.
164             addAsGetterMethodOverride(builder, builderFactory);
165             return;
166         }
167
168         constructGetter(builder, methodReturnType(builderFactory));
169     }
170
171     MethodSignatureBuilder constructGetter(final GeneratedTypeBuilderBase<?> builder, final Type returnType) {
172         final MethodSignatureBuilder getMethod = builder
173             .addMethod(BindingMapping.getGetterMethodName(localName().getLocalName()))
174             .setReturnType(returnType);
175
176         annotateDeprecatedIfNecessary(getMethod);
177 //        addComment(getMethod, node);
178
179         return getMethod;
180     }
181
182     void addAsGetterMethodOverride(final @NonNull GeneratedTypeBuilderBase<?> builder,
183             final @NonNull TypeBuilderFactory builderFactory) {
184         // No-op for most cases
185     }
186
187     @NonNull Type methodReturnType(final @NonNull TypeBuilderFactory builderFactory) {
188         return getGeneratedType(builderFactory);
189     }
190
191     final void annotateDeprecatedIfNecessary(final AnnotableTypeBuilder builder) {
192         annotateDeprecatedIfNecessary(statement, builder);
193     }
194
195     @Override
196     ToStringHelper addToStringAttributes(final ToStringHelper helper) {
197         helper.add("argument", statement.argument());
198
199         if (isAddedByUses()) {
200             helper.addValue("addedByUses");
201         }
202         if (isAugmenting()) {
203             helper.addValue("augmenting");
204         }
205         return helper;
206     }
207 }