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.util.Types.STRING;
13 import static org.opendaylight.mdsal.binding.model.util.Types.classType;
14 import static org.opendaylight.mdsal.binding.model.util.Types.primitiveBooleanType;
15 import static org.opendaylight.mdsal.binding.model.util.Types.primitiveIntType;
16 import static org.opendaylight.mdsal.binding.model.util.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.DefaultType;
29 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
30 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
31 import org.opendaylight.mdsal.binding.model.api.Type;
32 import org.opendaylight.mdsal.binding.model.api.YangSourceDefinition;
33 import org.opendaylight.mdsal.binding.model.api.type.builder.AnnotableTypeBuilder;
34 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedPropertyBuilder;
35 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTOBuilder;
36 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilder;
37 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilderBase;
38 import org.opendaylight.mdsal.binding.model.api.type.builder.MethodSignatureBuilder;
39 import org.opendaylight.mdsal.binding.model.util.BindingGeneratorUtil;
40 import org.opendaylight.mdsal.binding.model.util.BindingTypes;
41 import org.opendaylight.mdsal.binding.model.util.TypeComments;
42 import org.opendaylight.mdsal.binding.model.util.Types;
43 import org.opendaylight.mdsal.binding.model.util.generated.type.builder.GeneratedPropertyBuilderImpl;
44 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
45 import org.opendaylight.yangtools.yang.binding.DataContainer;
46 import org.opendaylight.yangtools.yang.model.api.DocumentedNode;
47 import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
48 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
49 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
50 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
51 import org.opendaylight.yangtools.yang.model.ri.type.TypeBuilder;
52 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
55 * A single node in generator tree. Each node will eventually resolve to a generated Java class. Each node also can have
56 * a number of children, which are generators corresponding to the YANG subtree of this node.
58 public abstract class Generator implements Iterable<Generator> {
59 private static final JavaTypeName DEPRECATED_ANNOTATION = JavaTypeName.create(Deprecated.class);
60 static final JavaTypeName OVERRIDE_ANNOTATION = JavaTypeName.create(Override.class);
62 private final AbstractCompositeGenerator<?> parent;
64 private Optional<Member> member;
65 private GeneratorResult result;
66 private JavaTypeName typeName;
67 private String javaPackage;
73 Generator(final AbstractCompositeGenerator<?> parent) {
74 this.parent = requireNonNull(parent);
77 public final @NonNull Optional<GeneratedType> generatedType() {
78 return Optional.ofNullable(result.generatedType());
81 public @NonNull List<GeneratedType> auxiliaryGeneratedTypes() {
86 public Iterator<Generator> iterator() {
87 return Collections.emptyIterator();
91 * Return the {@link AbstractCompositeGenerator} inside which this generator is defined. It is illegal to call this
92 * method on a {@link ModuleGenerator}.
94 * @return Parent generator
96 final @NonNull AbstractCompositeGenerator<?> getParent() {
97 return verifyNotNull(parent, "No parent for %s", this);
104 @Nullable Generator findGenerator(final EffectiveStatement<?, ?> stmt) {
108 final @NonNull Generator getGenerator(final EffectiveStatement<?, ?> stmt) {
109 return verifyNotNull(findGenerator(stmt), "Cannot match statement %s in %s", stmt, this);
113 * Return the namespace of this statement.
115 * @return Corresponding namespace
116 * @throws UnsupportedOperationException if this node does not have a corresponding namespace
118 @NonNull StatementNamespace namespace() {
119 return StatementNamespace.DEFAULT;
122 @NonNull ModuleGenerator currentModule() {
123 return getParent().currentModule();
127 * Push this statement into a {@link SchemaInferenceStack} so that the stack contains a resolvable {@code data tree}
130 * @param inferenceStack Target inference stack
132 abstract void pushToInference(@NonNull SchemaInferenceStack inferenceStack);
134 abstract @NonNull ClassPlacement classPlacement();
136 final @NonNull Member getMember() {
137 return verifyNotNull(ensureMember(), "No member for %s", this);
140 final Member ensureMember() {
141 if (member == null) {
142 final ClassPlacement placement = classPlacement();
145 member = Optional.empty();
150 member = Optional.of(createMember(parentDomain()));
153 throw new IllegalStateException("Unhandled placement " + placement);
156 return member.orElse(null);
159 @NonNull CollisionDomain parentDomain() {
160 return getParent().domain();
163 abstract @NonNull Member createMember(@NonNull CollisionDomain domain);
166 * Create the type associated with this builder. This method idempotent.
168 * @param builderFactory Factory for {@link TypeBuilder}s
169 * @throws NullPointerException if {@code builderFactory} is {@code null}
171 final void ensureType(final TypeBuilderFactory builderFactory) {
172 if (result != null) {
176 final ClassPlacement placement = classPlacement();
180 result = GeneratorResult.empty();
183 result = GeneratorResult.member(createTypeImpl(requireNonNull(builderFactory)));
186 result = GeneratorResult.toplevel(createTypeImpl(requireNonNull(builderFactory)));
189 throw new IllegalStateException("Unhandled placement " + placement);
192 for (Generator child : this) {
193 child.ensureType(builderFactory);
197 @NonNull GeneratedType getGeneratedType(final TypeBuilderFactory builderFactory) {
198 return verifyNotNull(tryGeneratedType(builderFactory), "No type generated for %s", this);
201 final @Nullable GeneratedType tryGeneratedType(final TypeBuilderFactory builderFactory) {
202 ensureType(builderFactory);
203 return result.generatedType();
206 final @Nullable GeneratedType enclosedType(final TypeBuilderFactory builderFactory) {
207 ensureType(builderFactory);
208 return result.enclosedType();
212 * Create the type associated with this builder, as per {@link #ensureType(TypeBuilderFactory)} contract. This
213 * method is guaranteed to be called at most once.
215 * @param builderFactory Factory for {@link TypeBuilder}s
217 abstract @NonNull GeneratedType createTypeImpl(@NonNull TypeBuilderFactory builderFactory);
219 final @NonNull String assignedName() {
220 return getMember().currentClass();
223 final @NonNull String javaPackage() {
224 String local = javaPackage;
226 javaPackage = local = createJavaPackage();
231 @NonNull String createJavaPackage() {
232 final String parentPackage = getPackageParent().javaPackage();
233 final String myPackage = getMember().currentPackage();
234 return BindingMapping.normalizePackageName(parentPackage + '.' + myPackage);
237 final @NonNull JavaTypeName typeName() {
238 JavaTypeName local = typeName;
240 typeName = local = createTypeName();
245 @NonNull JavaTypeName createTypeName() {
246 return JavaTypeName.create(getPackageParent().javaPackage(), assignedName());
249 @NonNull AbstractCompositeGenerator<?> getPackageParent() {
254 public final String toString() {
255 return addToStringAttributes(MoreObjects.toStringHelper(this).omitNullValues()).toString();
258 ToStringHelper addToStringAttributes(final ToStringHelper helper) {
262 final void addImplementsChildOf(final GeneratedTypeBuilder builder) {
263 AbstractCompositeGenerator<?> ancestor = getParent();
265 // choice/case hierarchy does not factor into 'ChildOf' hierarchy, hence we need to skip them
266 if (ancestor instanceof CaseGenerator || ancestor instanceof ChoiceGenerator) {
267 ancestor = ancestor.getParent();
271 // if we into a choice we need to follow the hierararchy of that choice
272 if (ancestor instanceof AbstractAugmentGenerator) {
273 final AbstractCompositeGenerator<?> target = ((AbstractAugmentGenerator) ancestor).targetGenerator();
274 if (target instanceof ChoiceGenerator) {
283 builder.addImplementsType(BindingTypes.childOf(DefaultType.of(ancestor.typeName())));
287 * Add common methods implemented in a generated type. This includes {@link DataContainer#implementedInterface()} as
288 * well has {@code bindingHashCode()}, {@code bindingEquals()} and {@code bindingToString()}.
290 * @param builder Target builder
292 static final void addConcreteInterfaceMethods(final GeneratedTypeBuilder builder) {
293 defaultImplementedInterace(builder);
295 builder.addMethod(BindingMapping.BINDING_HASHCODE_NAME)
296 .setAccessModifier(AccessModifier.PUBLIC)
298 .setReturnType(primitiveIntType());
299 builder.addMethod(BindingMapping.BINDING_EQUALS_NAME)
300 .setAccessModifier(AccessModifier.PUBLIC)
302 .setReturnType(primitiveBooleanType());
303 builder.addMethod(BindingMapping.BINDING_TO_STRING_NAME)
304 .setAccessModifier(AccessModifier.PUBLIC)
306 .setReturnType(STRING);
309 static final void annotateDeprecatedIfNecessary(final EffectiveStatement<?, ?> stmt,
310 final AnnotableTypeBuilder builder) {
311 if (stmt instanceof WithStatus) {
312 annotateDeprecatedIfNecessary((WithStatus) stmt, builder);
316 static final void annotateDeprecatedIfNecessary(final WithStatus node, final AnnotableTypeBuilder builder) {
317 switch (node.getStatus()) {
319 // FIXME: we really want to use a pre-made annotation
320 builder.addAnnotation(DEPRECATED_ANNOTATION);
323 builder.addAnnotation(DEPRECATED_ANNOTATION).addParameter("forRemoval", "true");
329 throw new IllegalStateException("Unhandled status in " + node);
333 static final void addCodegenInformation(final EffectiveStatement<?, ?> stmt,
334 final GeneratedTypeBuilderBase<?> builder) {
335 if (stmt instanceof DocumentedNode) {
336 addCodegenInformation((DocumentedNode) stmt, builder);
340 static final void addCodegenInformation(final DocumentedNode node, final GeneratedTypeBuilderBase<?> builder) {
341 node.getDescription().map(BindingGeneratorUtil::encodeAngleBrackets).ifPresent(builder::setDescription);
342 node.getReference().ifPresent(builder::setReference);
345 static final void addCodegenInformation(final ModuleGenerator module, final EffectiveStatement<?, ?> stmt,
346 final GeneratedTypeBuilderBase<?> builder) {
347 if (stmt instanceof DocumentedNode) {
348 final DocumentedNode node = (DocumentedNode) stmt;
349 TypeComments.description(node).ifPresent(builder::addComment);
350 node.getDescription().ifPresent(builder::setDescription);
351 node.getReference().ifPresent(builder::setReference);
353 if (stmt instanceof SchemaNode) {
354 YangSourceDefinition.of(module.statement(), (SchemaNode) stmt).ifPresent(builder::setYangSourceDefinition);
358 static final void addUnits(final GeneratedTOBuilder builder, final TypeDefinition<?> typedef) {
359 typedef.getUnits().ifPresent(units -> {
360 if (!units.isEmpty()) {
361 builder.addConstant(Types.STRING, "_UNITS", "\"" + units + "\"");
362 final GeneratedPropertyBuilder prop = new GeneratedPropertyBuilderImpl("UNITS");
363 prop.setReturnType(Types.STRING);
364 builder.addToStringProperty(prop);
370 * Add {@link java.io.Serializable} to implemented interfaces of this TO. Also compute and add serialVersionUID
373 * @param builder transfer object which needs to be made serializable
375 static final void makeSerializable(final GeneratedTOBuilder builder) {
376 builder.addImplementsType(Types.serializableType());
377 addSerialVersionUID(builder);
380 static final void addSerialVersionUID(final GeneratedTOBuilder gto) {
381 final GeneratedPropertyBuilder prop = new GeneratedPropertyBuilderImpl("serialVersionUID");
382 prop.setValue(Long.toString(BindingGeneratorUtil.computeDefaultSUID(gto)));
387 * Add a {@link DataContainer#implementedInterface()} declaration with a narrower return type to specified builder.
389 * @param builder Target builder
391 static final void narrowImplementedInterface(final GeneratedTypeBuilder builder) {
392 defineImplementedInterfaceMethod(builder, wildcardTypeFor(builder.getIdentifier()));
396 * Add a default implementation of {@link DataContainer#implementedInterface()} to specified builder.
398 * @param builder Target builder
400 static final void defaultImplementedInterace(final GeneratedTypeBuilder builder) {
401 defineImplementedInterfaceMethod(builder, DefaultType.of(builder)).setDefault(true);
404 static final <T extends EffectiveStatement<?, ?>> AbstractExplicitGenerator<T> getChild(final Generator parent,
405 final Class<T> type) {
406 for (Generator child : parent) {
407 if (child instanceof AbstractExplicitGenerator) {
408 @SuppressWarnings("unchecked")
409 final AbstractExplicitGenerator<T> explicit = (AbstractExplicitGenerator<T>)child;
410 if (type.isInstance(explicit.statement())) {
415 throw new IllegalStateException("Cannot find " + type + " in " + parent);
418 private static MethodSignatureBuilder defineImplementedInterfaceMethod(final GeneratedTypeBuilder typeBuilder,
419 final Type classType) {
420 final MethodSignatureBuilder ret = typeBuilder
421 .addMethod(BindingMapping.DATA_CONTAINER_IMPLEMENTED_INTERFACE_NAME)
422 .setAccessModifier(AccessModifier.PUBLIC)
423 .setReturnType(classType(classType));
424 ret.addAnnotation(OVERRIDE_ANNOTATION);