Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pico anno processor rewrite #6705

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,8 @@ static Optional<TypeInfo> toTypeInfo(TypeElement element,
try {
Set<AnnotationAndValue> annotations =
createAnnotationAndValueSet(elementUtils.getTypeElement(genericTypeName.name()));
Map<TypeName, List<AnnotationAndValue>> referencedAnnotations = toMetaAnnotations(annotations, processingEnv);
Map<TypeName, List<AnnotationAndValue>> referencedAnnotations =
new LinkedHashMap<>(toMetaAnnotations(annotations, processingEnv));
List<TypedElementName> elementsWeCareAbout = new ArrayList<>();
List<TypedElementName> otherElements = new ArrayList<>();
element.getEnclosedElements().stream()
Expand All @@ -328,6 +329,9 @@ static Optional<TypeInfo> toTypeInfo(TypeElement element,
} else {
otherElements.add(it);
}
merge(referencedAnnotations, toMetaAnnotations(it.annotations(), processingEnv));
it.parameterArguments().forEach(arg -> merge(referencedAnnotations,
toMetaAnnotations(arg.annotations(), processingEnv)));
});
TypeInfoDefault.Builder builder = TypeInfoDefault.builder()
.typeName(fqTypeName)
Expand Down Expand Up @@ -412,6 +416,11 @@ static Optional<TypeInfo> toTypeInfo(TypeElement element,
}
}

private static void merge(Map<TypeName, List<AnnotationAndValue>> result,
Map<TypeName, List<AnnotationAndValue>> metaAnnotations) {
metaAnnotations.forEach((key1, value) -> result.computeIfAbsent(key1, (key) -> new ArrayList<>()).addAll(value));
}

/**
* Determines if the given type element is defined in the module being processed. If so then the return value is set to
* {@code true} and the moduleName is cleared out. If not then the return value is set to {@code false} and the
Expand Down Expand Up @@ -720,44 +729,49 @@ public static String extractValue(AnnotationMirror am,
public static Optional<TypedElementName> createTypedElementNameFromElement(Element v,
Elements elements) {
TypeName type = createTypeNameFromElement(v).orElse(null);
List<TypeName> componentTypeNames = null;
TypeMirror typeMirror = null;
String defaultValue = null;
List<AnnotationAndValue> elementTypeAnnotations = List.of();
Set<String> modifierNames = Set.of();
List<TypedElementName> params = List.of();
List<TypeName> componentTypeNames = List.of();
List<AnnotationAndValue> elementTypeAnnotations = List.of();
Set<String> modifierNames = v.getModifiers().stream()
.map(Modifier::toString)
.collect(Collectors.toSet());

if (v instanceof ExecutableElement) {
ExecutableElement ee = (ExecutableElement) v;
TypeMirror returnType = ee.getReturnType();
typeMirror = Objects.requireNonNull(ee.getReturnType());
params = ee.getParameters().stream()
.map(it -> createTypedElementNameFromElement(it, elements).orElseThrow())
.collect(Collectors.toList());
AnnotationValue annotationValue = ee.getDefaultValue();
defaultValue = (annotationValue == null) ? null
: annotationValue.accept(new ToStringAnnotationValueVisitor()
.mapBooleanToNull(true)
.mapVoidToNull(true)
.mapBlankArrayToNull(true)
.mapEmptyStringToNull(true)
.mapToSourceDeclaration(true), null);
} else if (v instanceof VariableElement) {
VariableElement ve = (VariableElement) v;
typeMirror = Objects.requireNonNull(ve.asType());
}

if (typeMirror != null) {
if (type == null) {
type = createTypeNameFromMirror(returnType).orElse(createFromGenericDeclaration(returnType.toString()));
type = createTypeNameFromMirror(typeMirror).orElse(createFromGenericDeclaration(typeMirror.toString()));
}
if (returnType instanceof DeclaredType) {
List<? extends TypeMirror> args = ((DeclaredType) returnType).getTypeArguments();
if (typeMirror instanceof DeclaredType) {
List<? extends TypeMirror> args = ((DeclaredType) typeMirror).getTypeArguments();
componentTypeNames = args.stream()
.map(BuilderTypeTools::createTypeNameFromMirror)
.filter(Optional::isPresent)
.map(Optional::orElseThrow)
.collect(Collectors.toList());
elementTypeAnnotations =
createAnnotationAndValueListFromElement(((DeclaredType) returnType).asElement(), elements);
createAnnotationAndValueListFromElement(((DeclaredType) typeMirror).asElement(), elements);
}
AnnotationValue annotationValue = ee.getDefaultValue();
defaultValue = annotationValue == null
? null
: annotationValue.accept(new ToStringAnnotationValueVisitor()
.mapBooleanToNull(true)
.mapVoidToNull(true)
.mapBlankArrayToNull(true)
.mapEmptyStringToNull(true)
.mapToSourceDeclaration(true), null);
modifierNames = ee.getModifiers().stream()
.map(Modifier::toString)
.collect(Collectors.toSet());
params = ee.getParameters().stream()
.map(it -> createTypedElementNameFromElement(it, elements).orElseThrow())
.collect(Collectors.toList());
}
componentTypeNames = (componentTypeNames == null) ? List.of() : componentTypeNames;

TypedElementNameDefault.Builder builder = TypedElementNameDefault.builder()
.typeName(type)
Expand All @@ -767,7 +781,7 @@ public static Optional<TypedElementName> createTypedElementNameFromElement(Eleme
.annotations(createAnnotationAndValueListFromElement(v, elements))
.elementTypeAnnotations(elementTypeAnnotations)
.modifierNames(modifierNames)
.parameterArgumentss(params);
.parameterArguments(params);
createTypeNameFromElement(v.getEnclosingElement()).ifPresent(builder::enclosingTypeName);
Optional.ofNullable(defaultValue).ifPresent(builder::defaultValue);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ public Builder enclosingTypeName(Class<?> val) {
* @param val the parameter values
* @return this fluent builder
*/
public Builder parameterArgumentss(List<TypedElementName> val) {
public Builder parameterArguments(List<TypedElementName> val) {
Objects.requireNonNull(val);
this.parameters.clear();
this.parameters.addAll(val);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package io.helidon.pico.api;

import java.lang.annotation.Annotation;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;

Expand Down Expand Up @@ -129,10 +130,17 @@ public static QualifierAndValue convert(AnnotationAndValue annotationAndValue) {
return (QualifierAndValue) annotationAndValue;
}

return (QualifierAndValue) builder()
// qualifiers should not have any blank values
Map<String, String> values = annotationAndValue.values();
String val = values.get("value");
if ("".equals(val)) {
values = new LinkedHashMap<>(values);
values.remove("value");
}

return builder()
.typeName(annotationAndValue.typeName())
.values(annotationAndValue.values())
.update(it -> annotationAndValue.value().ifPresent(it::value))
.values(values)
.build();
}

Expand Down
5 changes: 2 additions & 3 deletions pico/api/src/main/java/io/helidon/pico/api/RunLevel.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,19 @@
package io.helidon.pico.api;

import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.TYPE;

/**
* Indicates the desired startup sequence for a service class.
* Indicates the desired startup sequence for a service class. This is not used internally by Pico, but is available as a
* convenience to the caller in support for a specific startup sequence for service activations.
*/
@Documented
@Retention(RetentionPolicy.CLASS)
@Target(TYPE)
@Inherited
public @interface RunLevel {

/**
Expand Down
10 changes: 3 additions & 7 deletions pico/configdriven/processor/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,17 @@
<groupId>io.helidon.builder</groupId>
<artifactId>helidon-builder-config</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.pico.configdriven</groupId>
<artifactId>helidon-pico-configdriven-api</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.pico</groupId>
<artifactId>helidon-pico-api</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.pico.configdriven</groupId>
<artifactId>helidon-pico-configdriven-runtime</artifactId>
<groupId>io.helidon.pico</groupId>
<artifactId>helidon-pico-processor</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.pico</groupId>
<artifactId>helidon-pico-processor</artifactId>
<artifactId>helidon-pico-tools</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.builder</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* Copyright (c) 2023 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.pico.configdriven.processor;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import io.helidon.common.types.AnnotationAndValue;
import io.helidon.common.types.TypeInfo;
import io.helidon.common.types.TypeName;
import io.helidon.common.types.TypedElementName;
import io.helidon.pico.processor.PicoAnnotationProcessor;
import io.helidon.pico.tools.ActivatorCreatorProvider;
import io.helidon.pico.tools.ServicesToProcess;

import static io.helidon.common.types.TypeNameDefault.createFromGenericDeclaration;
import static io.helidon.common.types.TypeNameDefault.createFromTypeName;
import static io.helidon.common.types.TypeNameDefault.toBuilder;
import static io.helidon.pico.configdriven.processor.ConfiguredByProcessorUtils.createExtraActivatorClassComments;
import static io.helidon.pico.configdriven.processor.ConfiguredByProcessorUtils.createExtraCodeGen;
import static io.helidon.pico.tools.TypeNames.PICO_ABSTRACT_CONFIGURED_SERVICE_PROVIDER;
import static io.helidon.pico.tools.TypeNames.PICO_CONFIGURED_BY;

/**
* Extension to {@link PicoAnnotationProcessor} that will handle {@code io.helidon.pico.configdriven.api.ConfiguredBy} services.
*/
public class ConfiguredByAnnotationProcessor extends PicoAnnotationProcessor {

/**
* Service loader based constructor.
*
* @deprecated this is a Java ServiceLoader implementation and the constructor should not be used directly
*/
@Deprecated
public ConfiguredByAnnotationProcessor() {
super(true);
}

@Override
protected Set<String> supportedServiceClassTargetAnnotations() {
Set<String> supported = new HashSet<>(super.supportedServiceClassTargetAnnotations());
supported.add(PICO_CONFIGURED_BY);
return supported;
}

@Override
protected void processExtensions(ServicesToProcess services,
TypeInfo service,
Set<TypeName> serviceTypeNamesToCodeGenerate,
Collection<TypedElementName> allElementsOfInterest) {
Optional<? extends AnnotationAndValue> configuredByAnno = findFirst(PICO_CONFIGURED_BY, service.annotations());
if (configuredByAnno.isEmpty()) {
return;
}

Map<String, String> configuredByAttributes = configuredByAnno.get().values();
TypeName configBeanType = createFromTypeName(configuredByAttributes.get("value"));
TypeInfo parent = service.superTypeInfo().orElse(null);
boolean hasParent = (parent != null);
TypeName serviceTypeName = service.typeName();
TypeName parentServiceTypeName = (hasParent) ? parent.typeName() : null;
TypeName activatorImplTypeName = activatorCreator().toActivatorImplTypeName(serviceTypeName);
TypeName genericCB = createFromGenericDeclaration("CB");
TypeName genericExtendsCB = createFromGenericDeclaration("CB extends " + configBeanType.name());

if (hasParent && findFirst(PICO_CONFIGURED_BY, parent.annotations()).isEmpty()) {
// we treat this as a regular configured service, since its parent is NOT a configured service
hasParent = false;
parentServiceTypeName = null;
}

if (hasParent) {
// we already know our parent, but we need to morph it with our activator and new CB reference
TypeName parentActivatorImplTypeName = ActivatorCreatorProvider.instance()
.toActivatorImplTypeName(parentServiceTypeName);
parentServiceTypeName = toBuilder(parentActivatorImplTypeName)
.typeArguments(List.of(genericCB))
.build();
} else {
List<TypeName> typeArgs = List.of(serviceTypeName, genericCB);
parentServiceTypeName = createFromTypeName(PICO_ABSTRACT_CONFIGURED_SERVICE_PROVIDER).toBuilder()
.typeArguments(typeArgs)
.build();
}

List<String> extraCodeGen = createExtraCodeGen(activatorImplTypeName, configBeanType, hasParent, configuredByAttributes);

boolean accepted = services.addParentServiceType(serviceTypeName, parentServiceTypeName, Optional.of(true));
assert (accepted);
services.addActivatorGenericDecl(serviceTypeName, "<" + genericExtendsCB.fqName() + ">");
extraCodeGen.forEach(fn -> services.addExtraCodeGen(serviceTypeName, fn));

List<String> extraActivatorClassComments = createExtraActivatorClassComments();
extraActivatorClassComments.forEach(fn -> services.addExtraActivatorClassComments(serviceTypeName, fn));
}

}
Loading