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.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;
35 * An explicit {@link Generator}, associated with a particular {@link EffectiveStatement}.
37 public abstract class AbstractExplicitGenerator<T extends EffectiveStatement<?, ?>> extends Generator
38 implements CopyableNode {
39 private static final Logger LOG = LoggerFactory.getLogger(AbstractExplicitGenerator.class);
41 private final @NonNull T statement;
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;
50 AbstractExplicitGenerator(final T statement) {
51 this.statement = requireNonNull(statement);
54 AbstractExplicitGenerator(final T statement, final AbstractCompositeGenerator<?> parent) {
56 this.statement = requireNonNull(statement);
60 * Return the {@link EffectiveStatement} associated with this generator.
62 * @return An EffectiveStatement
64 public final @NonNull T statement() {
69 public final boolean isAddedByUses() {
70 return statement instanceof AddedByUsesAware && ((AddedByUsesAware) statement).isAddedByUses();
74 public final boolean isAugmenting() {
75 return statement instanceof CopyableNode && ((CopyableNode) statement).isAugmenting();
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);
86 final @Nullable AbstractExplicitGenerator<?> previous() {
90 @NonNull AbstractExplicitGenerator<?> getOriginal() {
91 return prev == null ? this : prev.getOriginal();
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())) {
107 final @NonNull AbstractExplicitGenerator<?> resolveSchemaNode(final @NonNull SchemaNodeIdentifier path,
108 final @Nullable QNameModule targetModule) {
109 AbstractExplicitGenerator<?> current = this;
110 QNameModule currentModule = targetModule;
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);
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;
128 final @NonNull QName getQName() {
129 final Object arg = statement.argument();
130 verify(arg instanceof QName, "Unexpected argument %s", arg);
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;
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;
149 Member createMember(final CollisionDomain domain) {
150 return domain.addPrimary(new CamelCaseNamingStrategy(namespace(), localName()));
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
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);
168 constructGetter(builder, methodReturnType(builderFactory));
171 MethodSignatureBuilder constructGetter(final GeneratedTypeBuilderBase<?> builder, final Type returnType) {
172 final MethodSignatureBuilder getMethod = builder
173 .addMethod(BindingMapping.getGetterMethodName(localName().getLocalName()))
174 .setReturnType(returnType);
176 annotateDeprecatedIfNecessary(getMethod);
177 // addComment(getMethod, node);
182 void addAsGetterMethodOverride(final @NonNull GeneratedTypeBuilderBase<?> builder,
183 final @NonNull TypeBuilderFactory builderFactory) {
184 // No-op for most cases
187 @NonNull Type methodReturnType(final @NonNull TypeBuilderFactory builderFactory) {
188 return getGeneratedType(builderFactory);
191 final void annotateDeprecatedIfNecessary(final AnnotableTypeBuilder builder) {
192 annotateDeprecatedIfNecessary(statement, builder);
196 ToStringHelper addToStringAttributes(final ToStringHelper helper) {
197 helper.add("argument", statement.argument());
199 if (isAddedByUses()) {
200 helper.addValue("addedByUses");
202 if (isAugmenting()) {
203 helper.addValue("augmenting");