Skip to content

Commit

Permalink
Merge pull request #19300 from gsmet/picocli-reflection
Browse files Browse the repository at this point in the history
Register classes referenced in Picocli annotations for reflection
  • Loading branch information
gsmet authored Aug 10, 2021

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
2 parents 589cebd + 8097af8 commit 85142f9
Showing 4 changed files with 52 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -4,17 +4,20 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.AnnotationValue.Kind;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.FieldInfo;
import org.jboss.jandex.IndexView;
import org.jboss.jandex.Type;
import org.jboss.logging.Logger;

import io.quarkus.deployment.annotations.BuildProducer;
@@ -32,9 +35,6 @@ public class PicocliNativeImageProcessor {

private static final Logger LOGGER = Logger.getLogger(PicocliNativeImageProcessor.class);

private static final DotName PARAMETERS = DotName.createSimple(CommandLine.Parameters.class.getName());
private static final String PARAMETERS_COMPLETION_CANDIDATES = "completionCandidates";

@BuildStep(onlyIf = NativeBuild.class)
void reflectionConfiguration(CombinedIndexBuildItem combinedIndexBuildItem,
BuildProducer<ReflectiveFieldBuildItem> reflectiveFields,
@@ -48,13 +48,15 @@ void reflectionConfiguration(CombinedIndexBuildItem combinedIndexBuildItem,
DotName.createSimple(CommandLine.Command.class.getName()),
DotName.createSimple(CommandLine.Mixin.class.getName()),
DotName.createSimple(CommandLine.Option.class.getName()),
PARAMETERS,
DotName.createSimple(CommandLine.Parameters.class.getName()),
DotName.createSimple(CommandLine.ParentCommand.class.getName()),
DotName.createSimple(CommandLine.Spec.class.getName()),
DotName.createSimple(CommandLine.Unmatched.class.getName()));

Set<ClassInfo> foundClasses = new HashSet<>();
Set<FieldInfo> foundFields = new HashSet<>();
Set<Type> typeAnnotationValues = new HashSet<>();

for (DotName analyzedAnnotation : annotationsToAnalyze) {
for (AnnotationInstance ann : index.getAnnotations(analyzedAnnotation)) {
AnnotationTarget target = ann.target();
@@ -78,13 +80,21 @@ void reflectionConfiguration(CombinedIndexBuildItem combinedIndexBuildItem,
LOGGER.warnf("Unsupported type %s annotated with %s", target.kind().name(), analyzedAnnotation);
break;
}

// register classes references in Picocli annotations for reflection
List<AnnotationValue> values = ann.valuesWithDefaults(index);
for (AnnotationValue value : values) {
if (value.kind() == Kind.CLASS) {
typeAnnotationValues.add(value.asClass());
} else if (value.kind() == Kind.ARRAY && value.componentKind() == Kind.CLASS) {
for (Type componentClass : value.asClassArray()) {
typeAnnotationValues.add(componentClass);
}
}
}
}
}

Arrays.asList(DotName.createSimple(CommandLine.IVersionProvider.class.getName()),
DotName.createSimple(CommandLine.class.getName()))
.forEach(interfaceName -> foundClasses.addAll(index.getAllKnownImplementors(interfaceName)));

foundClasses.forEach(classInfo -> {
if (Modifier.isInterface(classInfo.flags())) {
nativeImageProxies
@@ -97,22 +107,10 @@ void reflectionConfiguration(CombinedIndexBuildItem combinedIndexBuildItem,
}
});
foundFields.forEach(fieldInfo -> reflectiveFields.produce(new ReflectiveFieldBuildItem(fieldInfo)));

// register @Parameters(completionCandidates = ...) for reflection
Collection<AnnotationInstance> parametersAnnotationInstances = index
.getAnnotations(PARAMETERS);
for (AnnotationInstance parametersAnnotationInstance : parametersAnnotationInstances) {
AnnotationValue completionCandidates = parametersAnnotationInstance.value(PARAMETERS_COMPLETION_CANDIDATES);

if (completionCandidates == null) {
continue;
}

reflectiveHierarchies.produce(new ReflectiveHierarchyBuildItem.Builder()
.type(completionCandidates.asClass())
.source(PicocliNativeImageProcessor.class.getSimpleName() + " > " + parametersAnnotationInstance.target())
.build());
}
typeAnnotationValues.forEach(type -> reflectiveHierarchies.produce(new ReflectiveHierarchyBuildItem.Builder()
.type(type)
.source(PicocliNativeImageProcessor.class.getSimpleName())
.build()));
}

@BuildStep(onlyIf = NativeBuild.class)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.quarkus.it.picocli;

import picocli.CommandLine.IDefaultValueProvider;
import picocli.CommandLine.Model.ArgSpec;

public class CustomDefaultValueProvider implements IDefaultValueProvider {

@Override
public String defaultValue(ArgSpec argSpec) throws Exception {
return "false";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.quarkus.it.picocli;

import picocli.CommandLine;

@CommandLine.Command(name = "defaultvalueprovider", mixinStandardHelpOptions = true, defaultValueProvider = CustomDefaultValueProvider.class)
public class DefaultValueProviderCommand implements Runnable {

@Override
public void run() {
}

}
Original file line number Diff line number Diff line change
@@ -41,6 +41,7 @@ public String getTestResults() throws UnknownHostException {
testUnmatched();
testI18s();
testCompletionReflection();
testDefaultValueProvider();
return "OK";
}

@@ -115,4 +116,9 @@ private void testCompletionReflection() {
CommandLine completionReflectionCommand = new CommandLine(CompletionReflectionCommand.class, factory);
completionReflectionCommand.execute("one");
}

private void testDefaultValueProvider() {
CommandLine cmd = new CommandLine(DefaultValueProviderCommand.class, factory);
Assertions.assertThat(cmd.execute()).isZero();
}
}

0 comments on commit 85142f9

Please sign in to comment.