2 * Copyright (c) 2021 PANTHEON.tech, s.r.o. and others. All rights reserved.
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
8 package org.opendaylight.mdsal.binding.generator.impl.reactor;
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;
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;
38 * An explicit {@link Generator}, associated with a particular {@link EffectiveStatement}.
40 public abstract class AbstractExplicitGenerator<T extends EffectiveStatement<?, ?>> extends Generator
41 implements CopyableNode {
42 private static final Logger LOG = LoggerFactory.getLogger(AbstractExplicitGenerator.class);
44 private final @NonNull T statement;
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;
53 AbstractExplicitGenerator(final T statement) {
54 this.statement = requireNonNull(statement);
57 AbstractExplicitGenerator(final T statement, final AbstractCompositeGenerator<?> parent) {
59 this.statement = requireNonNull(statement);
63 * Return the {@link EffectiveStatement} associated with this generator.
65 * @return An EffectiveStatement
67 public final @NonNull T statement() {
72 public final boolean isAddedByUses() {
73 return statement instanceof AddedByUsesAware && ((AddedByUsesAware) statement).isAddedByUses();
77 public final boolean isAugmenting() {
78 return statement instanceof CopyableNode && ((CopyableNode) statement).isAugmenting();
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);
89 final @Nullable AbstractExplicitGenerator<?> previous() {
93 @NonNull AbstractExplicitGenerator<?> getOriginal() {
94 return prev == null ? this : prev.getOriginal();
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())) {
110 final @NonNull AbstractExplicitGenerator<?> resolveSchemaNode(final @NonNull SchemaNodeIdentifier path,
111 final @Nullable QNameModule targetModule) {
112 AbstractExplicitGenerator<?> current = this;
113 QNameModule currentModule = targetModule;
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);
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;
131 final @NonNull QName getQName() {
132 final Object arg = statement.argument();
133 verify(arg instanceof QName, "Unexpected argument %s", arg);
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;
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;
152 Member createMember(final CollisionDomain domain) {
153 return domain.addPrimary(this, new CamelCaseNamingStrategy(namespace(), localName()));
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
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);
171 final Type returnType = methodReturnType(builderFactory);
172 constructGetter(builder, returnType);
173 constructRequire(builder, returnType);
176 MethodSignatureBuilder constructGetter(final GeneratedTypeBuilderBase<?> builder, final Type returnType) {
177 return constructGetter(builder, returnType, BindingMapping.getGetterMethodName(localName().getLocalName()));
180 final MethodSignatureBuilder constructGetter(final GeneratedTypeBuilderBase<?> builder,
181 final Type returnType, final String methodName) {
182 final MethodSignatureBuilder getMethod = builder.addMethod(methodName).setReturnType(returnType);
184 annotateDeprecatedIfNecessary(getMethod);
186 statement.findFirstEffectiveSubstatementArgument(DescriptionEffectiveStatement.class)
187 .map(TypeMemberComment::referenceOf).ifPresent(getMethod::setComment);
192 void constructRequire(final GeneratedTypeBuilderBase<?> builder, final Type returnType) {
193 // No-op in most cases
196 final void constructRequireImpl(final GeneratedTypeBuilderBase<?> builder, final Type returnType) {
197 constructGetter(builder, returnType, BindingMapping.getRequireMethodName(localName().getLocalName()))
199 .setMechanics(ValueMechanics.NONNULL);
202 void addAsGetterMethodOverride(final @NonNull GeneratedTypeBuilderBase<?> builder,
203 final @NonNull TypeBuilderFactory builderFactory) {
204 // No-op for most cases
207 @NonNull Type methodReturnType(final @NonNull TypeBuilderFactory builderFactory) {
208 return getGeneratedType(builderFactory);
211 final void annotateDeprecatedIfNecessary(final AnnotableTypeBuilder builder) {
212 annotateDeprecatedIfNecessary(statement, builder);
216 ToStringHelper addToStringAttributes(final ToStringHelper helper) {
217 helper.add("argument", statement.argument());
219 if (isAddedByUses()) {
220 helper.addValue("addedByUses");
222 if (isAugmenting()) {
223 helper.addValue("augmenting");