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.verifyNotNull;
11 import static java.util.Objects.requireNonNull;
12 import static org.opendaylight.mdsal.binding.model.ri.Types.STRING;
13 import static org.opendaylight.mdsal.binding.model.ri.Types.classType;
14 import static org.opendaylight.mdsal.binding.model.ri.Types.primitiveBooleanType;
15 import static org.opendaylight.mdsal.binding.model.ri.Types.primitiveIntType;
16 import static org.opendaylight.mdsal.binding.model.ri.Types.wildcardTypeFor;
18 import com.google.common.base.MoreObjects;
19 import com.google.common.base.MoreObjects.ToStringHelper;
20 import java.util.Collections;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Optional;
24 import org.eclipse.jdt.annotation.NonNull;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.opendaylight.mdsal.binding.generator.impl.reactor.CollisionDomain.Member;
27 import org.opendaylight.mdsal.binding.model.api.AccessModifier;
28 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
29 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
30 import org.opendaylight.mdsal.binding.model.api.Type;
31 import org.opendaylight.mdsal.binding.model.api.type.builder.AnnotableTypeBuilder;
32 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedPropertyBuilder;
33 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTOBuilder;
34 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilder;
35 import org.opendaylight.mdsal.binding.model.api.type.builder.MethodSignatureBuilder;
36 import org.opendaylight.mdsal.binding.model.ri.BindingTypes;
37 import org.opendaylight.mdsal.binding.model.ri.Types;
38 import org.opendaylight.mdsal.binding.model.ri.generated.type.builder.GeneratedPropertyBuilderImpl;
39 import org.opendaylight.yangtools.yang.binding.DataContainer;
40 import org.opendaylight.yangtools.yang.binding.contract.Naming;
41 import org.opendaylight.yangtools.yang.binding.contract.StatementNamespace;
42 import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
43 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
44 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
45 import org.opendaylight.yangtools.yang.model.ri.type.TypeBuilder;
46 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
49 * A single node in generator tree. Each node will eventually resolve to a generated Java class. Each node also can have
50 * a number of children, which are generators corresponding to the YANG subtree of this node.
53 * Each tree is rooted in a {@link ModuleGenerator} and its organization follows roughly YANG {@code schema tree}
54 * layout, but with a twist coming from the reuse of generated interfaces from a {@code grouping} in the location of
55 * every {@code uses} encountered and also the corresponding backwards propagation of {@code augment} effects.
58 * Overall the tree layout guides the allocation of Java package and top-level class namespaces.
60 public abstract class Generator implements Iterable<Generator> {
61 static final JavaTypeName DEPRECATED_ANNOTATION = JavaTypeName.create(Deprecated.class);
62 static final JavaTypeName OVERRIDE_ANNOTATION = JavaTypeName.create(Override.class);
64 private final AbstractCompositeGenerator<?, ?> parent;
66 private Optional<Member> member;
67 private GeneratorResult result;
68 private JavaTypeName typeName;
69 private String javaPackage;
75 Generator(final AbstractCompositeGenerator<?, ?> parent) {
76 this.parent = requireNonNull(parent);
79 public final @Nullable GeneratedType generatedType() {
80 return result.generatedType();
83 public @NonNull List<GeneratedType> auxiliaryGeneratedTypes() {
88 public Iterator<Generator> iterator() {
89 return Collections.emptyIterator();
93 * Return the {@link AbstractCompositeGenerator} inside which this generator is defined. It is illegal to call this
94 * method on a {@link ModuleGenerator}.
96 * @return Parent generator
98 final @NonNull AbstractCompositeGenerator<?, ?> getParent() {
99 return verifyNotNull(parent, "No parent for %s", this);
107 * Return the namespace of this statement.
109 * @return Corresponding namespace
110 * @throws UnsupportedOperationException if this node does not have a corresponding namespace
112 abstract @NonNull StatementNamespace namespace();
114 @NonNull ModuleGenerator currentModule() {
115 return getParent().currentModule();
119 * Push this statement into a {@link SchemaInferenceStack} so that the stack contains a resolvable {@code data tree}
122 * @param inferenceStack Target inference stack
124 abstract void pushToInference(@NonNull SchemaInferenceStack inferenceStack);
126 abstract @NonNull ClassPlacement classPlacement();
128 final @NonNull Member getMember() {
129 return verifyNotNull(ensureMember(), "No member for %s", this);
132 final Member ensureMember() {
133 if (member == null) {
134 member = switch (classPlacement()) {
135 case NONE -> Optional.empty();
136 case MEMBER, PHANTOM, TOP_LEVEL -> Optional.of(createMember(parentDomain()));
139 return member.orElse(null);
142 @NonNull CollisionDomain parentDomain() {
143 return getParent().domain();
146 abstract @NonNull Member createMember(@NonNull CollisionDomain domain);
149 * Create the type associated with this builder. This method idempotent.
151 * @param builderFactory Factory for {@link TypeBuilder}s
152 * @throws NullPointerException if {@code builderFactory} is {@code null}
154 final void ensureType(final TypeBuilderFactory builderFactory) {
155 if (result != null) {
159 result = switch (classPlacement()) {
160 case NONE, PHANTOM -> GeneratorResult.empty();
161 case MEMBER -> GeneratorResult.member(createTypeImpl(requireNonNull(builderFactory)));
162 case TOP_LEVEL -> GeneratorResult.toplevel(createTypeImpl(requireNonNull(builderFactory)));
165 for (Generator child : this) {
166 child.ensureType(builderFactory);
170 @NonNull GeneratedType getGeneratedType(final TypeBuilderFactory builderFactory) {
171 return verifyNotNull(tryGeneratedType(builderFactory), "No type generated for %s", this);
174 final @Nullable GeneratedType tryGeneratedType(final TypeBuilderFactory builderFactory) {
175 ensureType(builderFactory);
176 return result.generatedType();
179 final @Nullable GeneratedType enclosedType(final TypeBuilderFactory builderFactory) {
180 ensureType(builderFactory);
181 return result.enclosedType();
185 * Create the type associated with this builder, as per {@link #ensureType(TypeBuilderFactory)} contract. This
186 * method is guaranteed to be called at most once.
188 * @param builderFactory Factory for {@link TypeBuilder}s
190 abstract @NonNull GeneratedType createTypeImpl(@NonNull TypeBuilderFactory builderFactory);
192 final @NonNull String assignedName() {
193 return getMember().currentClass();
196 final @NonNull String javaPackage() {
197 String local = javaPackage;
199 javaPackage = local = createJavaPackage();
204 @NonNull String createJavaPackage() {
205 final String parentPackage = getPackageParent().javaPackage();
206 final String myPackage = getMember().currentPackage();
207 return Naming.normalizePackageName(parentPackage + '.' + myPackage);
210 final @NonNull JavaTypeName typeName() {
211 JavaTypeName local = typeName;
213 typeName = local = createTypeName();
218 @NonNull JavaTypeName createTypeName() {
219 return JavaTypeName.create(getPackageParent().javaPackage(), assignedName());
222 @NonNull AbstractCompositeGenerator<?, ?> getPackageParent() {
227 public final String toString() {
228 return addToStringAttributes(MoreObjects.toStringHelper(this).omitNullValues()).toString();
231 ToStringHelper addToStringAttributes(final ToStringHelper helper) {
235 final void addImplementsChildOf(final GeneratedTypeBuilder builder) {
236 AbstractCompositeGenerator<?, ?> ancestor = getParent();
238 // choice/case hierarchy does not factor into 'ChildOf' hierarchy, hence we need to skip them
239 if (ancestor instanceof CaseGenerator || ancestor instanceof ChoiceGenerator) {
240 ancestor = ancestor.getParent();
244 // if we into a choice we need to follow the hierararchy of that choice
245 if (ancestor instanceof AbstractAugmentGenerator augment
246 && augment.targetGenerator() instanceof ChoiceGenerator targetChoice) {
247 ancestor = targetChoice;
254 builder.addImplementsType(BindingTypes.childOf(Type.of(ancestor.typeName())));
258 * Add common methods implemented in a generated type. This includes {@link DataContainer#implementedInterface()} as
259 * well has {@code bindingHashCode()}, {@code bindingEquals()} and {@code bindingToString()}.
261 * @param builder Target builder
263 static final void addConcreteInterfaceMethods(final GeneratedTypeBuilder builder) {
264 defaultImplementedInterace(builder);
266 builder.addMethod(Naming.BINDING_HASHCODE_NAME)
267 .setAccessModifier(AccessModifier.PUBLIC)
269 .setReturnType(primitiveIntType());
270 builder.addMethod(Naming.BINDING_EQUALS_NAME)
271 .setAccessModifier(AccessModifier.PUBLIC)
273 .setReturnType(primitiveBooleanType());
274 builder.addMethod(Naming.BINDING_TO_STRING_NAME)
275 .setAccessModifier(AccessModifier.PUBLIC)
277 .setReturnType(STRING);
280 static final void annotateDeprecatedIfNecessary(final EffectiveStatement<?, ?> stmt,
281 final AnnotableTypeBuilder builder) {
282 if (stmt instanceof WithStatus withStatus) {
283 annotateDeprecatedIfNecessary(withStatus, builder);
287 static final void annotateDeprecatedIfNecessary(final WithStatus node, final AnnotableTypeBuilder builder) {
288 switch (node.getStatus()) {
290 // FIXME: we really want to use a pre-made annotation
291 builder.addAnnotation(DEPRECATED_ANNOTATION);
292 case OBSOLETE -> builder.addAnnotation(DEPRECATED_ANNOTATION).addParameter("forRemoval", "true");
296 default -> throw new IllegalStateException("Unhandled status in " + node);
300 static final void addUnits(final GeneratedTOBuilder builder, final TypeDefinition<?> typedef) {
301 typedef.getUnits().ifPresent(units -> {
302 if (!units.isEmpty()) {
303 builder.addConstant(Types.STRING, "_UNITS", "\"" + units + "\"");
304 final GeneratedPropertyBuilder prop = new GeneratedPropertyBuilderImpl("UNITS");
305 prop.setReturnType(Types.STRING);
306 builder.addToStringProperty(prop);
312 * Add {@link java.io.Serializable} to implemented interfaces of this TO. Also compute and add serialVersionUID
315 * @param builder transfer object which needs to be made serializable
317 static final void makeSerializable(final GeneratedTOBuilder builder) {
318 builder.addImplementsType(Types.serializableType());
319 addSerialVersionUID(builder);
322 static final void addSerialVersionUID(final GeneratedTOBuilder gto) {
323 final GeneratedPropertyBuilder prop = new GeneratedPropertyBuilderImpl("serialVersionUID");
324 prop.setValue(Long.toString(SerialVersionHelper.computeDefaultSUID(gto)));
329 * Add a {@link DataContainer#implementedInterface()} declaration with a narrower return type to specified builder.
331 * @param builder Target builder
333 static final void narrowImplementedInterface(final GeneratedTypeBuilder builder) {
334 defineImplementedInterfaceMethod(builder, wildcardTypeFor(builder.getIdentifier()));
338 * Add a default implementation of {@link DataContainer#implementedInterface()} to specified builder.
340 * @param builder Target builder
342 static final void defaultImplementedInterace(final GeneratedTypeBuilder builder) {
343 defineImplementedInterfaceMethod(builder, Type.of(builder)).setDefault(true);
346 static final <T extends EffectiveStatement<?, ?>> AbstractExplicitGenerator<T, ?> getChild(final Generator parent,
347 final Class<T> type) {
348 for (Generator child : parent) {
349 if (child instanceof AbstractExplicitGenerator) {
350 @SuppressWarnings("unchecked")
351 final AbstractExplicitGenerator<T, ?> explicit = (AbstractExplicitGenerator<T, ?>)child;
352 if (type.isInstance(explicit.statement())) {
357 throw new IllegalStateException("Cannot find " + type + " in " + parent);
360 private static MethodSignatureBuilder defineImplementedInterfaceMethod(final GeneratedTypeBuilder typeBuilder,
361 final Type classType) {
362 final MethodSignatureBuilder ret = typeBuilder
363 .addMethod(Naming.BINDING_CONTRACT_IMPLEMENTED_INTERFACE_NAME)
364 .setAccessModifier(AccessModifier.PUBLIC)
365 .setReturnType(classType(classType));
366 ret.addAnnotation(OVERRIDE_ANNOTATION);