io.helidon.service.codegen.GenerateServiceDescriptor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of helidon-service-codegen Show documentation
Show all versions of helidon-service-codegen Show documentation
Code generation implementation for Helidon Service, to be used from annotation processing and from maven plugin
The newest version!
/*
* Copyright (c) 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.helidon.service.codegen;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import io.helidon.codegen.CodegenException;
import io.helidon.codegen.CodegenUtil;
import io.helidon.codegen.ElementInfoPredicates;
import io.helidon.codegen.classmodel.ClassModel;
import io.helidon.codegen.classmodel.Javadoc;
import io.helidon.codegen.classmodel.Method;
import io.helidon.codegen.classmodel.TypeArgument;
import io.helidon.common.Weight;
import io.helidon.common.Weighted;
import io.helidon.common.types.AccessModifier;
import io.helidon.common.types.Annotation;
import io.helidon.common.types.Annotations;
import io.helidon.common.types.ElementKind;
import io.helidon.common.types.Modifier;
import io.helidon.common.types.TypeInfo;
import io.helidon.common.types.TypeName;
import io.helidon.common.types.TypeNames;
import io.helidon.common.types.TypedElementInfo;
import static io.helidon.service.codegen.ServiceCodegenTypes.SERVICE_ANNOTATION_PROVIDER;
import static java.util.function.Predicate.not;
/**
* Generates a service descriptor.
*/
class GenerateServiceDescriptor {
static final TypeName SET_OF_TYPES = TypeName.builder(TypeNames.SET)
.addTypeArgument(TypeNames.TYPE_NAME)
.build();
private static final TypeName LIST_OF_DEPENDENCIES = TypeName.builder(TypeNames.LIST)
.addTypeArgument(ServiceCodegenTypes.SERVICE_DEPENDENCY)
.build();
private static final TypeName DESCRIPTOR_TYPE = TypeName.builder(ServiceCodegenTypes.SERVICE_DESCRIPTOR)
.addTypeArgument(TypeName.create("T"))
.build();
private static final TypedElementInfo DEFAULT_CONSTRUCTOR = TypedElementInfo.builder()
.typeName(TypeNames.OBJECT)
.accessModifier(AccessModifier.PUBLIC)
.kind(ElementKind.CONSTRUCTOR)
.build();
private static final TypeName ANY_GENERIC_TYPE = TypeName.builder(TypeNames.GENERIC_TYPE)
.addTypeArgument(TypeName.create("?"))
.build();
private final TypeName generator;
private final RegistryCodegenContext ctx;
private final Collection services;
private final TypeInfo typeInfo;
private final boolean autoAddContracts;
private GenerateServiceDescriptor(TypeName generator,
RegistryCodegenContext ctx,
Collection allServices,
TypeInfo service) {
this.generator = generator;
this.ctx = ctx;
this.services = allServices;
this.typeInfo = service;
this.autoAddContracts = ServiceOptions.AUTO_ADD_NON_CONTRACT_INTERFACES.value(ctx.options());
}
/**
* Generate a service descriptor for the provided service type info.
*
* @param generator type of the generator responsible for this event
* @param ctx context of code generation
* @param allServices all services processed in this round of processing
* @param service service to create a descriptor for
* @return class model builder of the service descriptor
*/
static ClassModel.Builder generate(TypeName generator,
RegistryCodegenContext ctx,
Collection allServices,
TypeInfo service) {
return new GenerateServiceDescriptor(generator, ctx, allServices, service)
.generate();
}
static List declareCtrParamsAndGetThem(Method.Builder method, List params) {
List constructorParams = params.stream()
.filter(it -> it.kind() == ElementKind.CONSTRUCTOR)
.toList();
// for each parameter, obtain its value from context
for (ParamDefinition param : constructorParams) {
method.addContent(param.declaredType())
.addContent(" ")
.addContent(param.ipParamName())
.addContent(" = ")
.update(it -> param.assignmentHandler().accept(it))
.addContentLine(";");
}
if (!params.isEmpty()) {
method.addContentLine("");
}
return constructorParams;
}
private ClassModel.Builder generate() {
TypeName serviceType = typeInfo.typeName();
if (typeInfo.kind() == ElementKind.INTERFACE) {
throw new CodegenException("We can only generated service descriptors for classes, interface was requested: ",
typeInfo.originatingElement().orElse(serviceType));
}
boolean isAbstractClass = typeInfo.elementModifiers().contains(Modifier.ABSTRACT)
&& typeInfo.kind() == ElementKind.CLASS;
SuperType superType = superType(typeInfo, services);
// this must result in generating a service descriptor file
TypeName descriptorType = ctx.descriptorType(serviceType);
List params = params(typeInfo, constructor(typeInfo));
ClassModel.Builder classModel = ClassModel.builder()
.copyright(CodegenUtil.copyright(generator,
serviceType,
descriptorType))
.addAnnotation(CodegenUtil.generatedAnnotation(generator,
serviceType,
descriptorType,
"1",
""))
.type(descriptorType)
.addGenericArgument(TypeArgument.create("T extends " + serviceType.fqName()))
.javadoc(Javadoc.builder()
.add("Service descriptor for {@link " + serviceType.fqName() + "}.")
.addGenericArgument("T", "type of the service, for extensibility")
.build())
// we need to keep insertion order, as constants may depend on each other
.sortStaticFields(false);
Map genericTypes = genericTypes(classModel, params);
Set contracts = new HashSet<>();
Set collectedFullyQualifiedContracts = new HashSet<>();
contracts(typeInfo, autoAddContracts, contracts, collectedFullyQualifiedContracts);
// declare the class
if (superType.hasSupertype()) {
classModel.superType(superType.superDescriptorType());
} else {
classModel.addInterface(DESCRIPTOR_TYPE);
}
// Fields
singletonInstanceField(classModel, serviceType, descriptorType);
serviceTypeFields(classModel, serviceType, descriptorType);
// public fields are last, so they do not intersect with private fields (it is not as nice to read)
// they cannot be first, as they require some of the private fields
dependencyFields(classModel, typeInfo, genericTypes, params);
// dependencies require IP IDs, so they really must be last
dependenciesField(classModel, params);
// add protected constructor
classModel.addConstructor(constructor -> constructor.description("Constructor with no side effects")
.accessModifier(AccessModifier.PROTECTED));
// methods (some methods define fields as well)
serviceTypeMethod(classModel);
descriptorTypeMethod(classModel);
contractsMethod(classModel, contracts);
dependenciesMethod(classModel, params, superType);
isAbstractMethod(classModel, superType, isAbstractClass);
instantiateMethod(classModel, serviceType, params, isAbstractClass);
weightMethod(typeInfo, classModel, superType);
// service type is an implicit contract
Set allContracts = new HashSet<>(contracts);
allContracts.add(serviceType);
ctx.addDescriptor("core",
serviceType,
descriptorType,
classModel,
weight(typeInfo).orElse(Weighted.DEFAULT_WEIGHT),
allContracts,
typeInfo.originatingElement().orElseGet(typeInfo::typeName));
return classModel;
}
private SuperType superType(TypeInfo typeInfo, Collection services) {
// find super type if it is also a service (or has a service descriptor)
// check if the super type is part of current annotation processing
Optional superTypeInfoOptional = typeInfo.superTypeInfo();
if (superTypeInfoOptional.isEmpty()) {
return SuperType.noSuperType();
}
TypeInfo superType = superTypeInfoOptional.get();
TypeName expectedSuperDescriptor = ctx.descriptorType(superType.typeName());
TypeName superTypeToExtend = TypeName.builder(expectedSuperDescriptor)
.addTypeArgument(TypeName.create("T"))
.build();
boolean isCore = superType.hasAnnotation(SERVICE_ANNOTATION_PROVIDER);
if (!isCore) {
throw new CodegenException("Service annotated with @Service.Provider extends invalid supertype,"
+ " the super type must also be a @Service.Provider. Type: "
+ typeInfo.typeName().fqName() + ", super type: "
+ superType.typeName().fqName());
}
for (TypeInfo service : services) {
if (service.typeName().equals(superType.typeName())) {
return new SuperType(true, superTypeToExtend, service, true);
}
}
// if not found in current list, try checking existing types
return ctx.typeInfo(expectedSuperDescriptor)
.map(it -> new SuperType(true, superTypeToExtend, superType, true))
.orElseGet(SuperType::noSuperType);
}
// there must be none, or one non-private constructor (actually there may be more, we just use the first)
private TypedElementInfo constructor(TypeInfo typeInfo) {
return typeInfo.elementInfo()
.stream()
.filter(it -> it.kind() == ElementKind.CONSTRUCTOR)
.filter(not(ElementInfoPredicates::isPrivate))
.findFirst()
// or default constructor
.orElse(DEFAULT_CONSTRUCTOR);
}
private List params(
TypeInfo service,
TypedElementInfo constructor) {
AtomicInteger paramCounter = new AtomicInteger();
return constructor.parameterArguments()
.stream()
.map(param -> {
String constantName = "PARAM_" + paramCounter.getAndIncrement();
RegistryCodegenContext.Assignment assignment = translateParameter(param.typeName(), constantName);
return new ParamDefinition(constructor,
null,
param,
constantName,
param.typeName(),
assignment.usedType(),
assignment.codeGenerator(),
ElementKind.CONSTRUCTOR,
constructor.elementName(),
param.elementName(),
param.elementName(),
false,
param.annotations(),
Set.of(),
contract(service.typeName()
.fqName() + " Constructor parameter: " + param.elementName(),
assignment.usedType()),
constructor.accessModifier(),
"");
})
.toList();
}
private TypeName contract(String description, TypeName typeName) {
/*
get the contract expected for this dependency
IP may be:
- Optional
- List
- ServiceProvider
- Supplier
- Optional
- Optional
- List
- List
*/
if (typeName.isOptional()) {
if (typeName.typeArguments().isEmpty()) {
throw new IllegalArgumentException("Dependency with Optional type must have a declared type argument: "
+ description);
}
return contract(description, typeName.typeArguments().getFirst());
}
if (typeName.isList()) {
if (typeName.typeArguments().isEmpty()) {
throw new IllegalArgumentException("Dependency with List type must have a declared type argument: "
+ description);
}
return contract(description, typeName.typeArguments().getFirst());
}
if (typeName.isSupplier()) {
if (typeName.typeArguments().isEmpty()) {
throw new IllegalArgumentException("Dependency with Supplier type must have a declared type argument: "
+ description);
}
return contract(description, typeName.typeArguments().getFirst());
}
return typeName;
}
private Map genericTypes(ClassModel.Builder classModel,
List params) {
// we must use map by string (as type name is equal if the same class, not full generic declaration)
Map result = new LinkedHashMap<>();
AtomicInteger counter = new AtomicInteger();
for (ParamDefinition param : params) {
result.computeIfAbsent(param.translatedType().resolvedName(),
type -> {
var response =
new GenericTypeDeclaration("TYPE_" + counter.getAndIncrement(),
param.declaredType());
addTypeConstant(classModel, param.translatedType(), response);
return response;
});
result.computeIfAbsent(param.contract().fqName(),
type -> {
var response =
new GenericTypeDeclaration("TYPE_" + counter.getAndIncrement(),
param.declaredType());
addTypeConstant(classModel, param.contract(), response);
return response;
});
}
return result;
}
private void addTypeConstant(ClassModel.Builder classModel,
TypeName typeName,
GenericTypeDeclaration generic) {
String stringType = typeName.resolvedName();
// constants for dependency parameter types (used by next section)
classModel.addField(field -> field
.accessModifier(AccessModifier.PRIVATE)
.isStatic(true)
.isFinal(true)
.type(TypeNames.TYPE_NAME)
.name(generic.constantName())
.update(it -> {
if (stringType.indexOf('.') < 0) {
// there is no package, we must use class (if this is a generic type, we have a problem)
it.addContent(TypeNames.TYPE_NAME)
.addContent(".create(")
.addContent(typeName)
.addContent(".class)");
} else {
it.addContentCreate(typeName);
}
}));
classModel.addField(field -> field
.accessModifier(AccessModifier.PRIVATE)
.isStatic(true)
.isFinal(true)
.type(ANY_GENERIC_TYPE)
.name("G" + generic.constantName())
.update(it -> {
if (typeName.primitive()) {
it.addContent(TypeNames.GENERIC_TYPE)
.addContent(".create(")
.addContent(typeName.className())
.addContent(".class)");
} else {
it.addContent("new ")
.addContent(TypeNames.GENERIC_TYPE)
.addContent("<")
.addContent(typeName)
.addContent(">() {}");
}
})
);
}
private void contracts(TypeInfo typeInfo,
boolean contractEligible,
Set collectedContracts,
Set collectedFullyQualified) {
TypeName typeName = typeInfo.typeName();
boolean addedThisContract = false;
if (contractEligible) {
collectedContracts.add(typeName);
addedThisContract = true;
if (!collectedFullyQualified.add(typeName.resolvedName())) {
// let us go no further, this type was already processed
return;
}
}
if (typeName.isSupplier()) {
// this may be the interface itself, and then it does not have a type argument
if (!typeName.typeArguments().isEmpty()) {
// provider must have a type argument (and the type argument is an automatic contract
TypeName providedType = typeName.typeArguments().getFirst();
// and we support Supplier> as well
if (!providedType.generic()) {
// supplier is a contract
collectedContracts.add(TypeNames.SUPPLIER);
if (providedType.isOptional() && !providedType.typeArguments().isEmpty()) {
providedType = providedType.typeArguments().getFirst();
// we still have supplier as a contract
collectedFullyQualified.add(TypeNames.SUPPLIER.fqName());
collectedContracts.add(TypeNames.SUPPLIER);
if (providedType.generic()) {
providedType = null;
} else {
// and also optional
collectedFullyQualified.add(TypeNames.OPTIONAL.fqName());
collectedContracts.add(TypeNames.OPTIONAL);
contractsFromProvidedType(collectedContracts, collectedFullyQualified, providedType);
}
} else {
contractsFromProvidedType(collectedContracts, collectedFullyQualified, providedType);
}
if (providedType != null && !collectedFullyQualified.add(providedType.resolvedName())) {
// let us go no further, this type was already processed
return;
}
}
}
// provider itself is a contract
if (!addedThisContract) {
collectedContracts.add(typeName);
if (!collectedFullyQualified.add(typeName.resolvedName())) {
// let us go no further, this type was already processed
return;
}
}
}
// add contracts from interfaces and types annotated as @Contract
typeInfo.findAnnotation(ServiceCodegenTypes.SERVICE_ANNOTATION_CONTRACT)
.ifPresent(it -> collectedContracts.add(typeInfo.typeName()));
// add contracts from @ExternalContracts
typeInfo.findAnnotation(ServiceCodegenTypes.SERVICE_ANNOTATION_EXTERNAL_CONTRACTS)
.ifPresent(it -> collectedContracts.addAll(it.typeValues().orElseGet(List::of)));
// go through hierarchy
typeInfo.superTypeInfo().ifPresent(it -> contracts(it,
contractEligible,
collectedContracts,
collectedFullyQualified
));
// interfaces are considered contracts by default
typeInfo.interfaceTypeInfo().forEach(it -> contracts(it,
contractEligible,
collectedContracts,
collectedFullyQualified
));
}
private void contractsFromProvidedType(Set collectedContracts,
Set collectedFullyQualified,
TypeName providedType) {
Optional providedTypeInfo = ctx.typeInfo(providedType);
if (providedTypeInfo.isPresent()) {
contracts(providedTypeInfo.get(),
true,
collectedContracts,
collectedFullyQualified
);
} else {
collectedContracts.add(providedType);
}
}
private void singletonInstanceField(ClassModel.Builder classModel, TypeName serviceType, TypeName descriptorType) {
// singleton instance of the descriptor
classModel.addField(instance -> instance.description("Global singleton instance for this descriptor.")
.accessModifier(AccessModifier.PUBLIC)
.isStatic(true)
.isFinal(true)
.type(descriptorInstanceType(serviceType, descriptorType))
.name("INSTANCE")
.defaultValueContent("new " + descriptorType.className() + "<>()"));
}
private void serviceTypeFields(ClassModel.Builder classModel, TypeName serviceType, TypeName descriptorType) {
classModel.addField(field -> field
.isStatic(true)
.isFinal(true)
.accessModifier(AccessModifier.PRIVATE)
.type(TypeNames.TYPE_NAME)
.name("SERVICE_TYPE")
.addContentCreate(serviceType.genericTypeName()));
classModel.addField(field -> field
.isStatic(true)
.isFinal(true)
.accessModifier(AccessModifier.PRIVATE)
.type(TypeNames.TYPE_NAME)
.name("DESCRIPTOR_TYPE")
.addContentCreate(descriptorType.genericTypeName()));
}
private void dependencyFields(ClassModel.Builder classModel,
TypeInfo service,
Map genericTypes,
List params) {
// constant for dependency
for (ParamDefinition param : params) {
classModel.addField(field -> field
.accessModifier(AccessModifier.PUBLIC)
.isStatic(true)
.isFinal(true)
.type(ServiceCodegenTypes.SERVICE_DEPENDENCY)
.name(param.constantName())
.description(dependencyDescription(service, param))
.update(it -> {
it.addContent(ServiceCodegenTypes.SERVICE_DEPENDENCY)
.addContentLine(".builder()")
.increaseContentPadding()
.increaseContentPadding()
.addContent(".typeName(")
.addContent(genericTypes.get(param.translatedType().resolvedName()).constantName())
.addContentLine(")")
.update(maybeElementKind -> {
if (param.kind() != ElementKind.CONSTRUCTOR) {
// constructor is default and does not need to be defined
maybeElementKind.addContent(".elementKind(")
.addContent(TypeNames.ELEMENT_KIND)
.addContent(".")
.addContent(param.kind().name())
.addContentLine(")");
}
})
.update(maybeMethod -> {
if (param.kind() == ElementKind.METHOD) {
maybeMethod.addContent(".method(")
.addContent(param.methodConstantName())
.addContentLine(")");
}
})
.addContent(".name(\"")
.addContent(param.fieldId())
.addContentLine("\")")
.addContentLine(".service(SERVICE_TYPE)")
.addContentLine(".descriptor(DESCRIPTOR_TYPE)")
.addContent(".descriptorConstant(\"")
.addContent(param.constantName())
.addContentLine("\")")
.addContent(".contract(")
.addContent(genericTypes.get(param.contract().fqName()).constantName())
.addContentLine(")")
.addContent(".contractType(G")
.addContent(genericTypes.get(param.contract().fqName()).constantName())
.addContentLine(")");
if (param.access() != AccessModifier.PACKAGE_PRIVATE) {
it.addContent(".access(")
.addContent(TypeNames.ACCESS_MODIFIER)
.addContent(".")
.addContent(param.access().name())
.addContentLine(")");
}
if (param.isStatic()) {
it.addContentLine(".isStatic(true)");
}
if (!param.qualifiers().isEmpty()) {
for (Annotation qualifier : param.qualifiers()) {
it.addContent(".addQualifier(qualifier -> qualifier.typeName(")
.addContentCreate(qualifier.typeName().genericTypeName())
.addContent(")");
qualifier.value().ifPresent(q -> it.addContent(".value(\"")
.addContent(q)
.addContent("\")"));
it.addContentLine(")");
}
}
it.addContent(".build()")
.decreaseContentPadding()
.decreaseContentPadding();
}));
}
}
private String dependencyDescription(TypeInfo service, ParamDefinition param) {
TypeName serviceType = service.typeName();
StringBuilder result = new StringBuilder("Dependency for ");
boolean servicePublic = service.accessModifier() == AccessModifier.PUBLIC;
boolean elementPublic = param.owningElement().accessModifier() == AccessModifier.PUBLIC;
if (servicePublic) {
result.append("{@link ")
.append(serviceType.fqName());
if (!elementPublic) {
result.append("}");
}
} else {
result.append(serviceType.classNameWithEnclosingNames());
}
if (servicePublic && elementPublic) {
// full javadoc reference
result
.append("#")
.append(serviceType.className())
.append("(")
.append(toDescriptionSignature(param.owningElement(), true))
.append(")")
.append("}");
} else {
// just text
result.append("(")
.append(toDescriptionSignature(param.owningElement(), false))
.append(")");
}
result
.append(", parameter ")
.append(param.elementInfo().elementName())
.append(".");
return result.toString();
}
private String toDescriptionSignature(TypedElementInfo method, boolean javadoc) {
if (javadoc) {
return method.parameterArguments()
.stream()
.map(it -> it.typeName().fqName())
.collect(Collectors.joining(", "));
} else {
return method.parameterArguments()
.stream()
.map(it -> it.typeName().classNameWithEnclosingNames() + " " + it.elementName())
.collect(Collectors.joining(", "));
}
}
private void dependenciesField(ClassModel.Builder classModel, List params) {
classModel.addField(dependencies -> dependencies
.isStatic(true)
.isFinal(true)
.name("DEPENDENCIES")
.type(LIST_OF_DEPENDENCIES)
.addContent(List.class)
.addContent(".of(")
.update(it -> {
Iterator iterator = params.iterator();
while (iterator.hasNext()) {
it.addContent(iterator.next().constantName());
if (iterator.hasNext()) {
it.addContent(", ");
}
}
})
.addContent(")"));
}
private void serviceTypeMethod(ClassModel.Builder classModel) {
// TypeName serviceType()
classModel.addMethod(method -> method.addAnnotation(Annotations.OVERRIDE)
.returnType(TypeNames.TYPE_NAME)
.name("serviceType")
.addContentLine("return SERVICE_TYPE;"));
}
private void descriptorTypeMethod(ClassModel.Builder classModel) {
// TypeName descriptorType()
classModel.addMethod(method -> method.addAnnotation(Annotations.OVERRIDE)
.returnType(TypeNames.TYPE_NAME)
.name("descriptorType")
.addContentLine("return DESCRIPTOR_TYPE;"));
}
private void contractsMethod(ClassModel.Builder classModel, Set contracts) {
if (contracts.isEmpty()) {
return;
}
classModel.addField(contractsField -> contractsField
.isStatic(true)
.isFinal(true)
.name("CONTRACTS")
.type(SET_OF_TYPES)
.addContent(Set.class)
.addContent(".of(")
.update(it -> {
Iterator iterator = contracts.iterator();
while (iterator.hasNext()) {
it.addContentCreate(iterator.next().genericTypeName());
if (iterator.hasNext()) {
it.addContent(", ");
}
}
})
.addContent(")"));
// Set> contracts()
classModel.addMethod(method -> method.addAnnotation(Annotations.OVERRIDE)
.name("contracts")
.returnType(SET_OF_TYPES)
.addContentLine("return CONTRACTS;"));
}
private void dependenciesMethod(ClassModel.Builder classModel, List params, SuperType superType) {
// List dependencies()
boolean hasSuperType = superType.hasSupertype();
if (hasSuperType || !params.isEmpty()) {
classModel.addMethod(method -> method.addAnnotation(Annotations.OVERRIDE)
.returnType(LIST_OF_DEPENDENCIES)
.name("dependencies")
.update(it -> {
if (hasSuperType) {
it.addContentLine("return combineDependencies(DEPENDENCIES, super.dependencies());");
} else {
it.addContentLine("return DEPENDENCIES;");
}
}));
}
}
private void instantiateMethod(ClassModel.Builder classModel,
TypeName serviceType,
List params,
boolean isAbstractClass) {
if (isAbstractClass) {
return;
}
// T instantiate(DependencyContext ctx__helidonRegistry)
classModel.addMethod(method -> method.addAnnotation(Annotations.OVERRIDE)
.returnType(serviceType)
.name("instantiate")
.addParameter(ctxParam -> ctxParam.type(ServiceCodegenTypes.SERVICE_DEPENDENCY_CONTEXT)
.name("ctx__helidonRegistry"))
.update(it -> createInstantiateBody(serviceType, it, params)));
}
private void createInstantiateBody(TypeName serviceType,
Method.Builder method,
List params) {
List constructorParams = declareCtrParamsAndGetThem(method, params);
String paramsDeclaration = constructorParams.stream()
.map(ParamDefinition::ipParamName)
.collect(Collectors.joining(", "));
// return new MyImpl(parameter, parameter2)
method.addContent("return new ")
.addContent(serviceType.genericTypeName())
.addContent("(")
.addContent(paramsDeclaration)
.addContentLine(");");
}
private void isAbstractMethod(ClassModel.Builder classModel, SuperType superType, boolean isAbstractClass) {
if (!isAbstractClass && !superType.hasSupertype()) {
return;
}
// only override for abstract types (and subtypes, where we do not want to check if super is abstract), default is false
classModel.addMethod(isAbstract -> isAbstract
.name("isAbstract")
.returnType(TypeNames.PRIMITIVE_BOOLEAN)
.addAnnotation(Annotations.OVERRIDE)
.addContentLine("return " + isAbstractClass + ";"));
}
private void weightMethod(TypeInfo typeInfo, ClassModel.Builder classModel, SuperType superType) {
boolean hasSuperType = superType.hasSupertype();
// double weight()
Optional weight = weight(typeInfo);
if (!hasSuperType && weight.isEmpty()) {
return;
}
double usedWeight = weight.orElse(Weighted.DEFAULT_WEIGHT);
if (!hasSuperType && usedWeight == Weighted.DEFAULT_WEIGHT) {
return;
}
classModel.addMethod(weightMethod -> weightMethod.name("weight")
.addAnnotation(Annotations.OVERRIDE)
.returnType(TypeNames.PRIMITIVE_DOUBLE)
.addContentLine("return " + usedWeight + ";"));
}
private Optional weight(TypeInfo typeInfo) {
return typeInfo.findAnnotation(TypeName.create(Weight.class))
.flatMap(Annotation::doubleValue);
}
private RegistryCodegenContext.Assignment translateParameter(TypeName typeName, String constantName) {
return ctx.assignment(typeName, "ctx__helidonRegistry.dependency(" + constantName + ")");
}
private TypeName descriptorInstanceType(TypeName serviceType, TypeName descriptorType) {
return TypeName.builder(descriptorType)
.addTypeArgument(serviceType)
.build();
}
private record GenericTypeDeclaration(String constantName,
TypeName typeName) {
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy