Skip to content

Commit

Permalink
#500 Add picocli annotation processor
Browse files Browse the repository at this point in the history
* add separate module picocli-annotation-processing-tests for testing (com.google.testing.compile:compile-testing requires Java 8)
* (API) add ITypeInfo that encapsulated type information available at both runtime and compile time
* add RuntimeTypeInfo implementation used at runtime (reflection)
* (API) add IAnnotatedElement as public API for method and fields annotated with any picocli annotation
* internal class TypedMember now implements IAnnotatedElement
* (API) CommandSpec.resourceBundleBaseName() getter and setter
* (API) Messages.resourceBundleBaseName() getter
* (API) ArgSpec.userObject() getter
* (API) made MethodParam public
* IGetter/ISetter implementations now have informative toString() methods
* removed internal class ArgsReflection
  • Loading branch information
remkop committed Jan 30, 2019
1 parent b644a65 commit e8e1b44
Show file tree
Hide file tree
Showing 62 changed files with 7,784 additions and 355 deletions.
3 changes: 2 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
asciidoctorGradlePluginVersion = 1.5.3
compileTestingVersion = 0.15
gradleBintrayPluginVersion = 1.+
groovyVersion = 2.4.10
hamcrestCoreVersion = 1.3
Expand All @@ -14,7 +15,7 @@ junitVersion = 4.12
projectPreviousReleaseVersion = 3\\.9\\.2
# projectPreviousVersionRegex may be a SNAPSHOT
projectPreviousVersionRegex = 3\\.9\\.2
projectVersion = 3.9.3-SNAPSHOT
projectVersion = 4.0.0-alpha-1-SNAPSHOT

releaseDate = 2019-01-20
releaseDatePreviousRegex = 2019\\-01\\-20
Expand Down
59 changes: 59 additions & 0 deletions logging.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
############################################################
# Default Logging Configuration File
#
# You can use a different file by specifying a filename
# with the java.util.logging.config.file system property.
# For example java -Djava.util.logging.config.file=myfile
############################################################

############################################################
# Global properties
############################################################

# "handlers" specifies a comma separated list of log Handler
# classes. These handlers will be installed during VM startup.
# Note that these classes must be on the system classpath.
# By default we only configure a ConsoleHandler, which will only
# show messages at the INFO and above levels.
handlers= java.util.logging.ConsoleHandler

# To also add the FileHandler, use the following line instead.
#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler

# Default global logging level.
# This specifies which kinds of events are logged across
# all loggers. For any given facility this global level
# can be overriden by a facility specific level
# Note that the ConsoleHandler also has a separate level
# setting to limit messages printed to the console.
.level= FINE

############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################

# default file output is in user's home directory.
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter

# Limit the message that are printed on the console to INFO and above.
java.util.logging.ConsoleHandler.level = FINE
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

# Example to customize the SimpleFormatter output format
# to print one-line log message like this:
# <level>: <log message> [<date/time>]
#
# java.util.logging.SimpleFormatter.format=%4$s: %5$s [%1$tc]%n

############################################################
# Facility specific properties.
# Provides extra control for each logger.
############################################################

# For example, set the com.xyz.foo logger to only log SEVERE
# messages:
com.xyz.foo.level = SEVERE
28 changes: 28 additions & 0 deletions picocli-annotation-processing-tests/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
plugins {
id 'java'
}

group 'info.picocli'
description 'Picocli Annotation Processing Tests - Tests Annotation Processors for picocli Annotations.'
version "$projectVersion"
sourceCompatibility = 1.8
targetCompatibility = 1.8

dependencies {
compile rootProject
compile project(':picocli-codegen')
testCompile "junit:junit:$junitVersion",
"com.google.testing.compile:compile-testing:$compileTestingVersion",
files(org.gradle.internal.jvm.Jvm.current().getToolsJar()) // workaround https://github.com/google/compile-testing/issues/102 (and #28)
}
jar {
manifest {
attributes 'Specification-Title': 'Picocli Annotation Processing Tests',
'Specification-Vendor' : 'Remko Popma',
'Specification-Version' : version,
'Implementation-Title' : 'Picocli Annotation Processing Tests',
'Implementation-Vendor' : 'Remko Popma',
'Implementation-Version': version,
'Automatic-Module-Name' : 'info.picocli.annotation.processing.tests'
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package picocli.annotation.processing.tests;

import picocli.CommandLine.Model.CommandSpec;
import picocli.codegen.annotation.processing.AbstractCommandSpecProcessor;

import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;


public class CommandSpec2YamlProcessor extends AbstractCommandSpecProcessor {

public List<String> strings = new ArrayList<String>();
public Map<Element, CommandSpec> commands;

@Override
protected boolean handleCommands(Map<Element, CommandSpec> commands,
Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
System.out.println(commands);
this.commands = commands;
CommandSpecYamlPrinter printer = new CommandSpecYamlPrinter();
for (Map.Entry<Element, CommandSpec> entry : commands.entrySet()) {
StringWriter sw = new StringWriter();
printer.print(entry.getValue(), new PrintWriter(sw));
strings.add(sw.toString());
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
package picocli.annotation.processing.tests;

import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Model.ArgSpec;
import picocli.CommandLine.Model.CommandSpec;
import picocli.CommandLine.Model.OptionSpec;
import picocli.CommandLine.Model.ParserSpec;
import picocli.CommandLine.Model.PositionalParamSpec;
import picocli.CommandLine.Model.UnmatchedArgsBinding;
import picocli.CommandLine.Model.UsageMessageSpec;
import picocli.CommandLine.Parameters;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;

/**
* Dumps a {@code CommandSpec} in YAML format.
*/
public class CommandSpecYamlPrinter {

public static void main(String... args) {
CommandLine.run(new App(), args);
}

static void print(Object userObject) {
print(CommandSpec.forAnnotatedObject(userObject));
}

static void print(CommandSpec spec) {
StringWriter sw = new StringWriter();
new CommandSpecYamlPrinter().print(spec, new PrintWriter(sw));
System.out.println(sw);
}

public void print(CommandSpec spec, PrintWriter pw) {
pw.println("---");
printCommandSpec(spec, "CommandSpec:", pw, " ", " ");
}
private void printCommandSpec(CommandSpec spec, String label, PrintWriter pw,
String initialIndent, String indent) {
pw.printf("%s%n", label);
pw.printf("%sname: '%s'%n", initialIndent, spec.name());
pw.printf("%saliases: %s%n", indent, Arrays.toString(spec.aliases()));
pw.printf("%suserObject: %s%n", indent, spec.userObject());
pw.printf("%shelpCommand: %s%n", indent, spec.helpCommand());
pw.printf("%sdefaultValueProvider: %s%n", indent, spec.defaultValueProvider());
pw.printf("%sversionProvider: %s%n", indent, spec.versionProvider());
pw.printf("%sversion: %s%n", indent, Arrays.toString(spec.version()));

List<OptionSpec> options = new ArrayList<OptionSpec>(spec.options());
Collections.sort(options, new Comparator<OptionSpec>() {
public int compare(OptionSpec o1, OptionSpec o2) {
return o1.shortestName().compareTo(o2.shortestName());
}
});
printOptionList(options, pw, indent);
printPositionalList(spec.positionalParameters(), pw, indent);
printUnmatchedArgsBindingList(spec.unmatchedArgsBindings(), pw, indent);
printMixinList(spec.mixins(), pw, indent);

printUsageMessage(spec.usageMessage(), pw, indent);
printParser(spec.parser(), pw, indent);
printResourceBundle(spec.resourceBundle(), pw, indent);

printSubcommandList(spec.subcommands(), pw, indent);
}

private void printResourceBundle(ResourceBundle resourceBundle, PrintWriter pw, String indent) {
if (resourceBundle == null) {
return;
}
pw.printf("%sResourceBundle:%n", indent);
indent += " ";
for (Enumeration<String> keys = resourceBundle.getKeys(); keys.hasMoreElements();) {
String key = keys.nextElement();
pw.printf("%s%s: '%s'%n", indent, key, resourceBundle.getString(key));
}
}

private void printParser(ParserSpec parser, PrintWriter pw, String indent) {
pw.printf("%sParserSpec:%n", indent);
indent += " ";
pw.printf("%sseparator: '%s'%n", indent, parser.separator());
pw.printf("%sendOfOptionsDelimiter: '%s'%n", indent, parser.endOfOptionsDelimiter());
pw.printf("%sexpandAtFiles: %s%n", indent, parser.expandAtFiles());
pw.printf("%satFileCommentChar: '%s'%n", indent, parser.atFileCommentChar());
pw.printf("%soverwrittenOptionsAllowed: %s%n", indent, parser.overwrittenOptionsAllowed());
pw.printf("%sunmatchedArgumentsAllowed: %s%n", indent, parser.unmatchedArgumentsAllowed());
pw.printf("%sunmatchedOptionsArePositionalParams: %s%n", indent, parser.unmatchedOptionsArePositionalParams());
pw.printf("%sstopAtUnmatched: %s%n", indent, parser.stopAtUnmatched());
pw.printf("%sstopAtPositional: %s%n", indent, parser.stopAtPositional());
pw.printf("%sposixClusteredShortOptionsAllowed: %s%n", indent, parser.posixClusteredShortOptionsAllowed());
pw.printf("%saritySatisfiedByAttachedOptionParam: %s%n", indent, parser.aritySatisfiedByAttachedOptionParam());
pw.printf("%scaseInsensitiveEnumValuesAllowed: %s%n", indent, parser.caseInsensitiveEnumValuesAllowed());
pw.printf("%scollectErrors: %s%n", indent, parser.collectErrors());
pw.printf("%slimitSplit: %s%n", indent, parser.limitSplit());
pw.printf("%stoggleBooleanFlags: %s%n", indent, parser.toggleBooleanFlags());
}

private void printUsageMessage(UsageMessageSpec usageMessage, PrintWriter pw, String indent) {
pw.printf("%sUsageMessageSpec:%n", indent);
indent += " ";
pw.printf("%swidth: %s%n", indent, usageMessage.width());
pw.printf("%sabbreviateSynopsis: %s%n", indent, usageMessage.abbreviateSynopsis());
pw.printf("%shidden: %s%n", indent, usageMessage.hidden());
pw.printf("%sshowDefaultValues: %s%n", indent, usageMessage.showDefaultValues());
pw.printf("%ssortOptions: %s%n", indent, usageMessage.sortOptions());
pw.printf("%srequiredOptionMarker: '%s'%n", indent, usageMessage.requiredOptionMarker());
pw.printf("%sheaderHeading: '%s'%n", indent, usageMessage.headerHeading());
pw.printf("%sheader: %s%n", indent, Arrays.toString(usageMessage.header()));
pw.printf("%ssynopsisHeading: '%s'%n", indent, usageMessage.synopsisHeading());
pw.printf("%scustomSynopsis: %s%n", indent, Arrays.toString(usageMessage.customSynopsis()));
pw.printf("%sdescriptionHeading: '%s'%n", indent, usageMessage.descriptionHeading());
pw.printf("%sdescription: %s%n", indent, Arrays.toString(usageMessage.description()));
pw.printf("%sparameterListHeading: '%s'%n", indent, usageMessage.parameterListHeading());
pw.printf("%soptionListHeading: '%s'%n", indent, usageMessage.optionListHeading());
pw.printf("%scommandListHeading: '%s'%n", indent, usageMessage.commandListHeading());
pw.printf("%sfooterHeading: '%s'%n", indent, usageMessage.footerHeading());
pw.printf("%sfooter: %s%n", indent, Arrays.toString(usageMessage.footer()));
}


private void printUnmatchedArgsBindingList(List<UnmatchedArgsBinding> unmatchedArgsBindings, PrintWriter pw, String indent) {
pw.printf("%sUnmatchedArgsBindings:", indent);
pw.println(unmatchedArgsBindings.isEmpty() ? " []" : "");
for (UnmatchedArgsBinding unmatched : unmatchedArgsBindings) {
pw.printf("%sgetter: %s%n", indent + "- ", unmatched.getter());
pw.printf("%ssetter: %s%n", indent + " ", unmatched.setter());
}
}

private void printMixinList(Map<String, CommandSpec> mixins, PrintWriter pw, String indent) {
pw.printf("%sMixins:", indent);
pw.println(mixins.isEmpty() ? " []" : "");
for (Map.Entry<String, CommandSpec> entry : mixins.entrySet()) {
printCommandSpec(entry.getValue(), indent + "# " + entry.getKey(), pw, indent + "- ", indent + " ");
}
}

private void printSubcommandList(Map<String, CommandLine> subcommands, PrintWriter pw, String indent) {
pw.printf("%sSubcommands:", indent);
pw.println(subcommands.isEmpty() ? " []" : "");
for (Map.Entry<String, CommandLine> entry : subcommands.entrySet()) {
printCommandSpec(entry.getValue().getCommandSpec(),
indent + "# " + entry.getKey(), pw, indent + "- ", indent + " ");
}
}

private void printOptionList(List<OptionSpec> options, PrintWriter pw, String indent) {
pw.printf("%sOptions:", indent);
pw.println(options.isEmpty() ? " []" : "");
for (OptionSpec option : options) {
printOption(option, pw, indent);
}
}
private void printOption(OptionSpec option, PrintWriter pw, String indent) {
pw.printf("%snames: %s%n", indent + "- ", Arrays.toString(option.names()));
indent += " ";
pw.printf("%susageHelp: %s%n", indent, option.usageHelp());
pw.printf("%sversionHelp: %s%n", indent, option.versionHelp());
printArg(option, pw, indent);
}

private void printPositionalList(List<PositionalParamSpec> positionals, PrintWriter pw, String indent) {
pw.printf("%sPositionalParams:", indent);
pw.println(positionals.isEmpty() ? " []" : "");
for (PositionalParamSpec positional : positionals) {
printPositional(positional, pw, indent);
}
}
private void printPositional(PositionalParamSpec positional, PrintWriter pw, String indent) {
pw.printf("%sindex: %s%n", indent + "- ", positional.index());
indent += " ";
printArg(positional, pw, indent);
}
private void printArg(ArgSpec arg, PrintWriter pw, String indent) {
pw.printf("%sdescription: %s%n", indent, Arrays.toString(arg.description()));
pw.printf("%sdescriptionKey: '%s'%n", indent, arg.descriptionKey());
pw.printf("%stypeInfo: %s%n", indent, arg.typeInfo());
pw.printf("%sarity: %s%n", indent, arg.arity());
pw.printf("%ssplitRegex: '%s'%n", indent, arg.splitRegex());
pw.printf("%sinteractive: %s%n", indent, arg.interactive());
pw.printf("%srequired: %s%n", indent, arg.required());
pw.printf("%shidden: %s%n", indent, arg.hidden());
pw.printf("%shideParamSyntax: %s%n", indent, arg.hideParamSyntax());
pw.printf("%sdefaultValue: '%s'%n", indent, arg.defaultValue());
pw.printf("%sshowDefaultValue: %s%n", indent, arg.showDefaultValue());
pw.printf("%shasInitialValue: %s%n", indent, arg.hasInitialValue());
pw.printf("%sinitialValue: '%s'%n", indent, arg.initialValue());
pw.printf("%sparamLabel: '%s'%n", indent, arg.paramLabel());
pw.printf("%sconverters: %s%n", indent, Arrays.toString(arg.converters()));
pw.printf("%scompletionCandidates: %s%n", indent, iter(arg.completionCandidates()));
pw.printf("%sgetter: %s%n", indent, arg.getter());
pw.printf("%ssetter: %s%n", indent, arg.setter());
}

private String iter(Iterable<String> iterable) {
if (iterable == null) { return "null"; }
StringBuilder sb = new StringBuilder();
sb.append("[");
String sep = "";
for (String str : iterable) {
sb.append(sep).append(str);
sep = ", ";
}
return sb.append("]").toString();
}

@Command(name = "CommandSpecYamlPrinter", mixinStandardHelpOptions = true,
description = "Prints details of a CommandSpec")
private static class App implements Runnable {

@Parameters(arity = "1..*")
Class<?>[] classes = new Class[0];

// @Override (requires Java 6)
public void run() {
for (Class<?> cls : classes) {
StringWriter sw = new StringWriter();
new CommandSpecYamlPrinter().print(CommandSpec.forAnnotatedObject(cls), new PrintWriter(sw));
System.out.println(sw);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
picocli.annotation.processing.tests.CommandSpec2YamlProcessor
Loading

0 comments on commit e8e1b44

Please sign in to comment.