Repository: logging-log4j2 Updated Branches: refs/heads/master 57bbd8825 -> c2818beca
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c2818bec/log4j-core/src/test/java/org/apache/logging/log4j/core/util/picocli/CustomLayoutDemo.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/util/picocli/CustomLayoutDemo.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/util/picocli/CustomLayoutDemo.java new file mode 100644 index 0000000..1599da4 --- /dev/null +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/util/picocli/CustomLayoutDemo.java @@ -0,0 +1,265 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.logging.log4j.core.util.picocli; + +import org.apache.logging.log4j.core.util.picocli.CommandLine.Help.Ansi.Text; +import org.apache.logging.log4j.core.util.picocli.CommandLine.Help.Column; +import org.apache.logging.log4j.core.util.picocli.CommandLine.Help.IOptionRenderer; +import org.apache.logging.log4j.core.util.picocli.CommandLine.Help.IParameterRenderer; +import org.apache.logging.log4j.core.util.picocli.CommandLine.Help.Layout; +import org.apache.logging.log4j.core.util.picocli.CommandLine.Help.TextTable; + +import java.awt.Point; +import java.lang.reflect.Field; + +import static org.apache.logging.log4j.core.util.picocli.CommandLine.*; +import static org.apache.logging.log4j.core.util.picocli.CommandLine.Help.Column.Overflow.*; +import static org.apache.logging.log4j.core.util.picocli.CommandLine.Help.Column.Overflow.SPAN; +import static org.apache.logging.log4j.core.util.picocli.CommandLine.Help.Column.Overflow.TRUNCATE; +import static org.apache.logging.log4j.core.util.picocli.CommandLine.Help.Column.Overflow.WRAP; + +/** + * Demonstrates how the CommandLine.Help API can be used to create custom layouts for usage help messages. + */ +@Command(name = "picocli.CustomLayoutDemo", description = "Demonstrates picocli custom layouts.", + footer = { + "Run with -Dpicocli.ansi=true to force picocli to use ansi codes,", + "or with -Dpicocli.ansi=false to force picocli to NOT use ansi codes.", + "By default picocli will use ansi codes if the platform supports it." + } +) +public class CustomLayoutDemo implements Runnable { + public static void main(String[] args) { + CommandLine.run(new CustomLayoutDemo(), System.err, args); + } + + @Option(names = {"-z", "--zip"}, description = "Show usage help for a layout with 2 options per row.") + private boolean showZip; + + @Option(names = {"-n", "--netstat"}, description = "Show usage help for a layout with a narrow options column and a wide description column. Descriptions that wrap to the next row are not indented.") + private boolean showNetstat; + + public void run() { + if (!showZip && !showNetstat) { + CommandLine.usage(this, System.err); + return; + } + if (showZip) { System.out.println(createZipUsageFormat(Help.Ansi.AUTO)); } + if (showNetstat) { System.out.println(createNetstatUsageFormat(Help.Ansi.AUTO)); } + } + + + public static String createZipUsageFormat(Help.Ansi ansi) { + @Command(description = { + "Copyright (c) 1990-2008 Info-ZIP - Type 'zip \"-L\"' for software license.", + "Zip 3.0 (July 5th 2008). Command:", + "@|bold zip|@ [@|yellow -options|@] [@|yellow -b|@ @|underline path|@] [@|yellow -t|@ @|underline mmddyyyy|@] [@|yellow -n|@ @|underline suffixes|@] [@|yellow zipfile|@ @|underline list|@] [@|yellow -xi|@ @|underline list|@]", + " The default action is to add or replace zipfile entries from list, which", + " can include the special name - to compress standard input.", + " If @|yellow zipfile|@ and @|yellow list|@ are omitted, zip compresses stdin to stdout."} + ) + class Zip { + @Option(names = "-f", description = "freshen: only changed files") + boolean freshen; + @Option(names = "-u", description = "update: only changed or new files") + boolean update; + @Option(names = "-d", description = "delete entries in zipfile") + boolean delete; + @Option(names = "-m", description = "move into zipfile (delete OS files)") + boolean move; + @Option(names = "-r", description = "recurse into directories") + boolean recurse; + @Option(names = "-j", description = "junk (don't record) directory names") + boolean junk; + @Option(names = "-0", description = "store only") + boolean store; + @Option(names = "-l", description = "convert LF to CR LF (@|yellow -ll|@ CR LF to LF)") + boolean lf2crlf; + @Option(names = "-1", description = "compress faster") + boolean faster; + @Option(names = "-9", description = "compress better") + boolean better; + @Option(names = "-q", description = "quiet operation") + boolean quiet; + @Option(names = "-v", description = "verbose operation/print version info") + boolean verbose; + @Option(names = "-c", description = "add one-line comments") + boolean comments; + @Option(names = "-z", description = "add zipfile comment") + boolean zipComment; + @Option(names = "-@", description = "read names from stdin") + boolean readFileList; + @Option(names = "-o", description = "make zipfile as old as latest entry") + boolean old; + @Option(names = "-x", description = "exclude the following names") + boolean exclude; + @Option(names = "-i", description = "include only the following names") + boolean include; + @Option(names = "-F", description = "fix zipfile (@|yellow -FF|@ try harder)") + boolean fix; + @Option(names = "-D", description = "do not add directory entries") + boolean directories; + @Option(names = "-A", description = "adjust self-extracting exe") + boolean adjust; + @Option(names = "-J", description = "junk zipfile prefix (unzipsfx)") + boolean junkPrefix; + @Option(names = "-T", description = "test zipfile integrity") + boolean test; + @Option(names = "-X", description = "eXclude eXtra file attributes") + boolean excludeAttribs; + @Option(names = "-y", description = "store symbolic links as the link instead of the referenced file") + boolean symbolic; + @Option(names = "-e", description = "encrypt") + boolean encrypt; + @Option(names = "-n", description = "don't compress these suffixes") + boolean dontCompress; + @Option(names = "-h2", description = "show more help") + boolean moreHelp; + } + + class TwoOptionsPerRowLayout extends Layout { // define a custom layout + Point previous = new Point(0, 0); + + private TwoOptionsPerRowLayout(Help.ColorScheme colorScheme, TextTable textTable, + IOptionRenderer optionRenderer, + IParameterRenderer parameterRenderer) { + super(colorScheme, textTable, optionRenderer, parameterRenderer); + } + + @Override + public void layout(Field field, Text[][] values) { + Text[] columnValues = values[0]; // we know renderer creates a single row with two values + + // We want to show two options on one row, next to each other, + // unless the first option spanned multiple columns (in which case there are not enough columns left) + int col = previous.x + 1; + if (col == 1 || col + columnValues.length > table.columns.length) { // if true, write into next row + + // table also adds an empty row if a text value spanned multiple columns + if (table.rowCount() == 0 || table.rowCount() == previous.y + 1) { // avoid adding 2 empty rows + table.addEmptyRow(); // create the slots to write the text values into + } + col = 0; // we are starting a new row, reset the column to write into + } + for (int i = 0; i < columnValues.length; i++) { + // always write to the last row, column depends on what happened previously + previous = table.putValue(table.rowCount() - 1, col + i, columnValues[i]); + } + } + } + TextTable textTable = new TextTable(ansi, + new Column(5, 2, TRUNCATE), // values should fit + new Column(30, 2, SPAN), // overflow into adjacent columns + new Column(4, 1, TRUNCATE), // values should fit again + new Column(39, 2, WRAP)); + TwoOptionsPerRowLayout layout = new TwoOptionsPerRowLayout( + Help.defaultColorScheme(ansi), + textTable, + Help.createMinimalOptionRenderer(), + Help.createMinimalParameterRenderer()); + + Help help = new Help(new Zip(), ansi); + StringBuilder sb = new StringBuilder(); + sb.append(help.description()); // show the first 6 lines, including copyright, description and usage + + // Note that we don't sort the options, so they appear in the order the fields are declared in the Zip class. + layout.addOptions(help.optionFields, help.parameterLabelRenderer); + sb.append(layout); // finally, copy the options details help text into the StringBuilder + + return sb.toString(); + } + + /** for Netstat test */ + private enum Protocol {IP, IPv6, ICMP, ICMPv6, TCP, TCPv6, UDP, UDPv6} + + public static String createNetstatUsageFormat(Help.Ansi ansi) { + @Command(name = "NETSTAT", + separator = " ", + abbreviateSynopsis = true, + header = "Displays protocol statistics and current TCP/IP network connections.%n") + class Netstat { + @Option(names="-a", description="Displays all connections and listening ports.") + boolean displayAll; + @Option(names="-b", description="Displays the executable involved in creating each connection or " + + "listening port. In some cases well-known executables host " + + "multiple independent components, and in these cases the " + + "sequence of components involved in creating the connection " + + "or listening port is displayed. In this case the executable " + + "name is in [] at the bottom, on top is the component it called, " + + "and so forth until TCP/IP was reached. Note that this option " + + "can be time-consuming and will fail unless you have sufficient " + + "permissions.") + boolean displayExecutable; + @Option(names="-e", description="Displays Ethernet statistics. This may be combined with the -s option.") + boolean displayEthernetStats; + @Option(names="-f", description="Displays Fully Qualified Domain Names (FQDN) for foreign addresses.") + boolean displayFQCN; + @Option(names="-n", description="Displays addresses and port numbers in numerical form.") + boolean displayNumerical; + @Option(names="-o", description="Displays the owning process ID associated with each connection.") + boolean displayOwningProcess; + @Option(names="-p", paramLabel = "proto", + description="Shows connections for the protocol specified by proto; proto " + + "may be any of: TCP, UDP, TCPv6, or UDPv6. If used with the -s " + + "option to display per-protocol statistics, proto may be any of: " + + "IP, IPv6, ICMP, ICMPv6, TCP, TCPv6, UDP, or UDPv6.") + Protocol proto; + @Option(names="-q", description="Displays all connections, listening ports, and bound " + + "nonlistening TCP ports. Bound nonlistening ports may or may not " + + "be associated with an active connection.") + boolean query; + @Option(names="-r", description="Displays the routing table.") + boolean displayRoutingTable; + @Option(names="-s", description="Displays per-protocol statistics. By default, statistics are " + + "shown for IP, IPv6, ICMP, ICMPv6, TCP, TCPv6, UDP, and UDPv6; " + + "the -p option may be used to specify a subset of the default.") + boolean displayStatistics; + @Option(names="-t", description="Displays the current connection offload state.") + boolean displayOffloadState; + @Option(names="-x", description="Displays NetworkDirect connections, listeners, and shared endpoints.") + boolean displayNetDirect; + @Option(names="-y", description="Displays the TCP connection template for all connections. " + + "Cannot be combined with the other options.") + boolean displayTcpConnectionTemplate; + @Parameters(arity = "0..1", paramLabel = "interval", description = "" + + "Redisplays selected statistics, pausing interval seconds " + + "between each display. Press CTRL+C to stop redisplaying " + + "statistics. If omitted, netstat will print the current " + + "configuration information once.") + int interval; + } + StringBuilder sb = new StringBuilder(); + Help help = new Help(new Netstat(), ansi); + help.synopsisHeading = ""; + sb.append(help.header()).append(help.detailedSynopsis(0, null, false)); + sb.append(System.getProperty("line.separator")); + + TextTable textTable = new TextTable(ansi, + new Column(15, 2, TRUNCATE), + new Column(65, 1, WRAP)); + textTable.indentWrappedLines = 0; + Layout layout = new Layout( + Help.defaultColorScheme(ansi), + textTable, + Help.createMinimalOptionRenderer(), + Help.createMinimalParameterRenderer()); + layout.addOptions(help.optionFields, help.parameterLabelRenderer); + layout.addPositionalParameters(help.positionalParametersFields, Help.createMinimalParamLabelRenderer()); + sb.append(layout); + return sb.toString(); + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c2818bec/log4j-core/src/test/java/org/apache/logging/log4j/core/util/picocli/Demo.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/util/picocli/Demo.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/util/picocli/Demo.java new file mode 100644 index 0000000..5845c8d --- /dev/null +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/util/picocli/Demo.java @@ -0,0 +1,652 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.logging.log4j.core.util.picocli; + +import org.apache.logging.log4j.core.util.picocli.CommandLine.Command; +import org.apache.logging.log4j.core.util.picocli.CommandLine.Option; +import org.apache.logging.log4j.core.util.picocli.CommandLine.Parameters; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; + +/** + * Demonstrates picocli subcommands. + */ +@Command(name = "picocli.Demo", sortOptions = false, + header = { + "@|green .__ .__ .__ |@", + "@|green ______ |__| ____ ____ ____ | | |__||@", + "@|green \\____ \\| |/ ___\\/ _ \\_/ ___\\| | | ||@", + "@|green | |_> > \\ \\__( <_> ) \\___| |_| ||@", + "@|green | __/|__|\\___ >____/ \\___ >____/__||@", + "@|green |__| \\/ \\/ |@", + ""}, + //descriptionHeading = "@|bold %nDescription|@:%n", + description = { + "", + "Demonstrates picocli subcommands parsing and usage help.", }, + optionListHeading = "@|bold %nOptions|@:%n", + footer = { + "", + "@|bold VM Options|@:", + "Run with @|yellow -ea|@ to enable assertions used in the tests.", + "Run with @|yellow -Dpicocli.ansi|@=@|italic true|@ to force picocli to use ansi codes,", + " or with @|yellow -Dpicocli.ansi|@=@|italic false|@ to force picocli to NOT use ansi codes.", + "(By default picocli will use ansi codes if the platform supports it.)", + "", + "@|cyan If you would like to contribute or report an issue|@", + "@|cyan go to github: https://github.com/remkop/picocli|@", + "", + "@|cyan If you like the project star it on github and follow me on twitter!|@", + "@|cyan This project is created and maintained by Remko Popma (@remkopopma)|@", + ""}) +public class Demo implements Runnable { + public static void main(String[] args) { + CommandLine.run(new Demo(), System.err, args); + } + + @Option(names = {"-1", "--showUsageForSubcommandGitCommit"}, description = "Shows usage help for the git-commit subcommand") + private boolean showUsageForSubcommandGitCommit; + + @Option(names = {"-2", "--showUsageForMainCommand"}, description = "Shows usage help for a command with subcommands") + private boolean showUsageForMainCommand; + + @Option(names = {"-3", "--showUsageForSubcommandGitStatus"}, description = "Shows usage help for the git-status subcommand") + private boolean showUsageForSubcommandGitStatus; + + @Option(names = "--simple", description = "Show help for the first simple Example in the manual") + private boolean showSimpleExample; + + @Option(names = "--mixed", hidden = true, description = "Show help with mixed Ansi colors and styles in description") + private boolean showAnsiInDescription; + + @Option(names = {"-i", "--index"}, description = "" + + "@|fg(21) S|@" + + "@|fg(57) h|@" + + "@|fg(93) o|@" + + "@|fg(129) w|@" + + "@|fg(129) |@" + + "@|fg(165) 2|@" + + "@|fg(201) 5|@" + + "@|fg(225) 6|@" + + "@|fg(123) |@" + + "@|fg(122) c|@" + + "@|fg(120) o|@" + + "@|fg(118) l|@" + + "@|fg(148) o|@" + + "@|fg(142) r|@" + + "@|fg(136) |@" + + "@|fg(136) p|@" + + "@|fg(130) a|@" + + "@|fg(124) l|@" + + "@|fg(160) e|@" + + "@|fg(196) t|@" + + "@|fg(198) t|@" + + "@|fg(199) e|@" + + "@|fg(200) |@" + + "@|fg(201) i|@" + + "@|fg(213) n|@" + + "@|fg(219) d|@" + + "@|fg(225) e|@" + + "@|fg(231) x|@" + + "@|fg(230) |@" + + "@|fg(229) v|@" + + "@|fg(228) a|@" + + "@|fg(227) l|@" + + "@|fg(226) u|@" + + "@|fg(190) e|@" + + "@|fg(154) s|@") + private boolean showIndexedColorPalette; + + @Option(names = {"-r", "--rgb"}, description = "" + + "@|fg(0;0;5) S|@" + + "@|fg(1;0;5) h|@" + + "@|fg(2;0;5) o|@" + + "@|fg(3;0;5) w|@" + + "@|fg(3;0;5) |@" + + "@|fg(4;0;5) 2|@" + + "@|fg(5;0;5) 5|@" + + "@|fg(5;4;5) 6|@" + + "@|fg(2;5;5) |@" + + "@|fg(2;5;4) c|@" + + "@|fg(2;5;2) o|@" + + "@|fg(2;5;0) l|@" + + "@|fg(3;4;0) o|@" + + "@|fg(3;3;0) r|@" + + "@|fg(3;2;0) |@" + + "@|fg(3;2;0) p|@" + + "@|fg(3;1;0) a|@" + + "@|fg(3;0;0) l|@" + + "@|fg(4;0;0) e|@" + + "@|fg(5;0;0) t|@" + + "@|fg(5;0;2) t|@" + + "@|fg(5;0;3) e|@" + + "@|fg(5;0;4) |@" + + "@|fg(5;0;5) R|@" + + "@|fg(5;2;5) G|@" + + "@|fg(5;3;5) B|@" + + "@|fg(5;4;5) |@" + + "@|fg(5;5;5) c|@" + + "@|fg(5;5;4) o|@" + + "@|fg(5;5;3) m|@" + + "@|fg(5;5;2) p|@" + + "@|fg(5;5;1) o|@" + + "@|fg(5;5;0) n|@" + + "@|fg(4;5;0) e|@" + + "@|fg(3;5;0) n|@" + + "@|fg(2;5;0) t|@" + + "@|fg(1;5;0) |@" + + "@|fg(1;5;0) v|@" + + "@|fg(0;5;0) a|@" + + "@|fg(0;4;0) l|@" + + "@|fg(0;3;0) u|@" + + "@|fg(0;2;0) e|@" + + "@|fg(0;1;0) s|@" + + "") + private boolean showRgbColorPalette; + + @Option(names = {"-t", "--tests"}, description = "Runs all tests in this class") + private boolean runTests; + + public void run() { + if (!runTests && + !showSimpleExample && + !showAnsiInDescription && + !showIndexedColorPalette && + !showRgbColorPalette && + !showUsageForMainCommand && + !showUsageForSubcommandGitCommit && + !showUsageForSubcommandGitStatus) { + CommandLine.usage(this, System.err); + return; + } + if (runTests) { testParseSubCommands(); System.out.println("Ran tests OK.");} + if (showSimpleExample) { showSimpleExampleUsage(); } + if (showAnsiInDescription) { showAnsiInDescription(); } + if (showIndexedColorPalette) { showIndexedColorPalette(); } + if (showRgbColorPalette) { showRgbColorPalette(); } + if (showUsageForMainCommand) { testUsageMainCommand(); } + if (showUsageForSubcommandGitStatus) { testUsageSubCommandStatus(); } + if (showUsageForSubcommandGitCommit) { testUsageSubCommandCommit(); } + } + + private void showSimpleExampleUsage() { + class Example { + @Option(names = { "-v", "--verbose" }, description = "Be verbose.") + private boolean verbose = false; + + @Option(names = { "-h", "--help" }, help = true, + description = "Displays this help message and quits.") + private boolean helpRequested = false; + + @Parameters(arity = "1..*", paramLabel = "FILE", description = "File(s) to process.") + private File[] inputFiles; + } + CommandLine.usage(new Example(), System.out); + } + + private void showAnsiInDescription() { + @Command(description = "Custom @|bold,underline styles|@ and @|fg(red) colors|@.") + class AnsiDescription { } + CommandLine.usage(new AnsiDescription(), System.out); + } + + private void showIndexedColorPalette() { + int[] foregroundBackground = {38, 48}; + for (int fbg : foregroundBackground) { + for (int r = 0; r < 2; r++) { + for (int g = 0; g < 6; g++) { + for (int b = 0; b < 6; b++) { + int col = 16 + 36 * (0 + 3 * r) + 6 * g + b; + System.out.printf("\u001B[%d;5;%dm%3d \u001B[0m", fbg, col, col); + } + for (int b = 0; b < 6; b++) { + int col = 16 + 36 * (1 + 3 * r) + 6 * g + b; + System.out.printf("\u001B[%d;5;%dm%3d \u001B[0m", fbg, col, col); + } + for (int b = 0; b < 6; b++) { + int col = 16 + 36 * (2 + 3 * r) + 6 * g + b; + System.out.printf("\u001B[%d;5;%dm%3d \u001B[0m", fbg, col, col); + } + System.out.println(); + } + System.out.println(); + } + int r = 6; + for (int g = 0; g < 4; g++) { + for (int b = 0; b < 6; b++) { + int col = 16 + 36 * r + 6 * g + b; + System.out.printf("\u001B[%d;5;%dm%3d \u001B[0m", fbg, col, col); + } + System.out.println(); + } + System.out.println(); + } + } + + private void showRgbColorPalette() { + int[] foregroundBackground = {38, 48}; + for (int fbg : foregroundBackground) { + for (int r = 0; r < 2; r++) { + System.out.println("RGB RGB RGB RGB RGB RGB RGB RGB RGB RGB RGB RGB RGB RGB RGB RGB RGB RGB "); + for (int g = 0; g < 6; g++) { + for (int b = 0; b < 6; b++) { + int col = 16 + 36 * (0 + 3 * r) + 6 * g + b; + System.out.printf("\u001B[%d;5;%dm%d%d%d \u001B[0m", fbg, col, (0 + 3 * r),g,b); + } + for (int b = 0; b < 6; b++) { + int col = 16 + 36 * (1 + 3 * r) + 6 * g + b; + System.out.printf("\u001B[%d;5;%dm%d%d%d \u001B[0m", fbg, col, (1 + 3 * r),g,b); + } + for (int b = 0; b < 6; b++) { + int col = 16 + 36 * (2 + 3 * r) + 6 * g + b; + System.out.printf("\u001B[%d;5;%dm%d%d%d \u001B[0m", fbg, col, (2 + 3 * r),g,b); + } + System.out.println(); + } + System.out.println(); + } + System.out.println("RGB RGB RGB RGB RGB RGB"); + int r = 6; + for (int g = 0; g < 4; g++) { + for (int b = 0; b < 6; b++) { + int col = 16 + 36 * r + 6 * g + b; + System.out.printf("\u001B[%d;5;%dm%d%d%d \u001B[0m", fbg, col, r,g,b); + } + System.out.println(); + } + System.out.println(); + } + } + + //------------------------------------------ + static + // tag::Git[] + // tag::Git-declaration[] + @Command(name = "git", sortOptions = false, + description = "Git is a fast, scalable, distributed revision control " + + "system with an unusually rich command set that provides both " + + "high-level operations and full access to internals.", + commandListHeading = "%nCommands:%n%nThe most commonly used git commands are:%n") + class Git { // end::Git-declaration[] + @Option(names = {"-V", "--version"}, help = true, description = "Prints version information and exits") + boolean isVersionRequested; + + @Option(names = {"-h", "--help"}, help = true, description = "Prints this help message and exits") + boolean isHelpRequested; + + @Option(names = "--git-dir", description = "Set the path to the repository") + File gitDir; + } + // end::Git[] + + // the "status" subcommand's has an option "mode" with a fixed number of values, modeled by this enum + enum GitStatusMode {all, no, normal}; + + static + @Command(name = "git-status", + header = "Show the working tree status.", + showDefaultValues = true, + customSynopsis = "@|bold git-status|@ [@|yellow <options>|@...] [--] [@|yellow <pathspec>|@...]", + description = "Displays paths that have differences between the index file and the current HEAD commit, " + + "paths that have differences between the working tree and the index file, and paths in the " + + "working tree that are not tracked by Git (and are not ignored by gitignore(5)). The first " + + "are what you would commit by running git commit; the second and third are what you could " + + "commit by running git add before running git commit." + ) + class GitStatus { + @Option(names = {"-s", "--short"}, description = "Give the output in the short-format") + boolean shortFormat; + + @Option(names = {"-b", "--branch"}, description = "Show the branch and tracking info even in short-format") + boolean branchInfo; + + @Option(names = "--ignored", description = "Show ignored files as well") boolean showIgnored; + + @Option(names = {"-u", "--untracked"}, paramLabel = "<mode>", description = { + "Show untracked files.", + "The mode parameter is optional (defaults to `all`), and is used to specify the handling of untracked files.", + "The possible options are:", + " * @|yellow no|@ - Show no untracked files.", + " * @|yellow normal|@ - Shows untracked files and directories.", + " * @|yellow all|@ - Also shows individual files in untracked directories." + }) + GitStatusMode mode = GitStatusMode.all; + } + + static + // tag::GitCommit[] + // tag::GitCommit-declaration[] + @Command(name = "git-commit", + sortOptions = false, + headerHeading = "@|bold,underline Usage:|@%n%n", + synopsisHeading = "%n", + descriptionHeading = "%n@|bold,underline Description:|@%n%n", + parameterListHeading = "%n@|bold,underline Parameters:|@%n", + optionListHeading = "%n@|bold,underline Options:|@%n", + header = "Record changes to the repository.", + description = "Stores the current contents of the index in a new commit " + + "along with a log message from the user describing the changes.") + class GitCommit { // end::GitCommit-declaration[] + @Option(names = {"-a", "--all"}, + description = "Tell the command to automatically stage files that have been modified " + + "and deleted, but new files you have not told Git about are not affected.") + boolean all; + + @Option(names = {"-p", "--patch"}, description = "Use the interactive patch selection interface to chose which changes to commit") + boolean patch; + + @Option(names = {"-C", "--reuse-message"}, paramLabel = "<commit>", + description = "Take an existing commit object, and reuse the log message and the " + + "authorship information (including the timestamp) when creating the commit.") + String reuseMessageCommit; + + @Option(names = {"-c", "--reedit-message"}, paramLabel = "<commit>", + description = "Like -C, but with -c the editor is invoked, so that the user can" + + "further edit the commit message.") + String reEditMessageCommit; + + @Option(names = "--fixup", paramLabel = "<commit>", + description = "Construct a commit message for use with rebase --autosquash.") + String fixupCommit; + + @Option(names = "--squash", paramLabel = "<commit>", + description = "Construct a commit message for use with rebase --autosquash. The commit" + + "message subject line is taken from the specified commit with a prefix of " + + "\"squash! \". Can be used with additional commit message options (-m/-c/-C/-F).") + String squashCommit; + + @Option(names = {"-F", "--file"}, paramLabel = "<file>", + description = "Take the commit message from the given file. Use - to read the message from the standard input.") + File file; + + @Option(names = {"-m", "--message"}, paramLabel = "<msg>", + description = " Use the given <msg> as the commit message. If multiple -m options" + + " are given, their values are concatenated as separate paragraphs.") + List<String> message = new ArrayList<String>(); + + @Parameters(paramLabel = "<files>", description = "the files to commit") + List<File> files = new ArrayList<File>(); + } + // end::GitCommit[] + + // defines some commands to show in the list (option/parameters fields omitted for this demo) + @Command(name = "git-add", header = "Add file contents to the index.") static class GitAdd {} + @Command(name = "git-branch", header = "List, create, or delete branches.") static class GitBranch {} + @Command(name = "git-checkout", header = "Checkout a branch or paths to the working tree.") static class GitCheckout{} + @Command(name = "git-clone", header = "Clone a repository into a new directory.") static class GitClone{} + @Command(name = "git-diff", header = "Show changes between commits, commit and working tree, etc.") static class GitDiff{} + @Command(name = "git-merge", header = "Join two or more development histories together.") static class GitMerge{} + @Command(name = "git-push", header = "Update remote refs along with associated objects.") static class GitPush{} + @Command(name = "git-rebase", header = "Forward-port local commits to the updated upstream head.") static class GitRebase{} + @Command(name = "git-tag", header = "Create, list, delete or verify a tag object signed with GPG.") static class GitTag{} + + /** @see CommandLineTest#testParseSubCommands() The JUnit test implementation of this test. */ + public static void testParseSubCommands() { + CommandLine commandLine = mainCommand(); + + String[] args = { "--git-dir=/home/rpopma/picocli", "status", "-sbuno"}; + List<CommandLine> parsed = commandLine.parse(args); + assert parsed.size() == 2 : "found 2 commands"; + + assert parsed.get(0).getCommand().getClass() == Git.class; + assert parsed.get(1).getCommand().getClass() == GitStatus.class; + + Git git = (Git) parsed.get(0).getCommand(); + assert git.gitDir.equals(new File("/home/rpopma/picocli")); + + GitStatus status = (GitStatus) parsed.get(1).getCommand(); + assert status.shortFormat : "status -s"; + assert status.branchInfo : "status -b"; + assert !status.showIgnored : "status --showIgnored not specified"; + assert status.mode == GitStatusMode.no : "status -u=no"; + } + + static CommandLine mainCommand() { + CommandLine commandLine = new CommandLine(new Git()); + commandLine.addSubcommand("status", new GitStatus()); + commandLine.addSubcommand("commit", new GitCommit()); + commandLine.addSubcommand("add", new GitAdd()); + commandLine.addSubcommand("branch", new GitBranch()); + commandLine.addSubcommand("checkout", new GitCheckout()); + commandLine.addSubcommand("clone", new GitClone()); + commandLine.addSubcommand("diff", new GitDiff()); + commandLine.addSubcommand("merge", new GitMerge()); + commandLine.addSubcommand("push", new GitPush()); + commandLine.addSubcommand("rebase", new GitRebase()); + commandLine.addSubcommand("tag", new GitTag()); + return commandLine; + } + + public void testUsageMainCommand() { + CommandLine commandLine = mainCommand(); + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + commandLine.usage(new PrintStream(baos, true, "UTF8")); + String result = baos.toString("UTF8"); + System.out.println(result); + assert String.format(EXPECTED_USAGE_MAIN).equals(result); + } catch (UnsupportedEncodingException ex) { + throw new InternalError(ex.toString()); + } + } + static final String EXPECTED_USAGE_MAIN = "Usage: git [-hV] [--git-dir=<gitDir>]%n" + + "Git is a fast, scalable, distributed revision control system with an unusually%n" + + "rich command set that provides both high-level operations and full access to%n" + + "internals.%n" + + " -V, --version Prints version information and exits%n" + + " -h, --help Prints this help message and exits%n" + + " --git-dir=<gitDir> Set the path to the repository%n" + + "%n" + + "Commands:%n" + + "%n" + + "The most commonly used git commands are:%n" + + " status Show the working tree status.%n" + + " commit Record changes to the repository.%n" + + " add Add file contents to the index.%n" + + " branch List, create, or delete branches.%n" + + " checkout Checkout a branch or paths to the working tree.%n" + + " clone Clone a repository into a new directory.%n" + + " diff Show changes between commits, commit and working tree, etc.%n" + + " merge Join two or more development histories together.%n" + + " push Update remote refs along with associated objects.%n" + + " rebase Forward-port local commits to the updated upstream head.%n" + + " tag Create, list, delete or verify a tag object signed with GPG.%n"; + + static final String EXPECTED_USAGE_MAIN_ANSI = "Usage: @|bold git|@ [@|yellow -hV|@] [@|yellow --git-dir|@=@|italic <gitDir>|@]%n" + + "Git is a fast, scalable, distributed revision control system with an unusually%n" + + "rich command set that provides both high-level operations and full access to%n" + + "internals.%n" + + " @|yellow -V|@, @|yellow --version|@ Prints version information and exits%n" + + " @|yellow -h|@, @|yellow --help|@ Prints this help message and exits%n" + + " @|yellow --git-dir|@=@|italic <gitDir>|@ Set the path to the repository%n" + + "%n" + + "Commands:%n" + + "%n" + + "The most commonly used git commands are:%n" + + " @|bold status|@ Show the working tree status.%n" + + " @|bold commit|@ Record changes to the repository.%n" + + " @|bold add|@ Add file contents to the index.%n" + + " @|bold branch|@ List, create, or delete branches.%n" + + " @|bold checkout|@ Checkout a branch or paths to the working tree.%n" + + " @|bold clone|@ Clone a repository into a new directory.%n" + + " @|bold diff|@ Show changes between commits, commit and working tree, etc.%n" + + " @|bold merge|@ Join two or more development histories together.%n" + + " @|bold push|@ Update remote refs along with associated objects.%n" + + " @|bold rebase|@ Forward-port local commits to the updated upstream head.%n" + + " @|bold tag|@ Create, list, delete or verify a tag object signed with GPG.%n"; + + public void testUsageSubCommandStatus() { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + CommandLine.usage(new GitStatus(), new PrintStream(baos, true, "UTF8")); + String result = baos.toString("UTF8"); + System.out.println(result); + assert String.format(EXPECTED_USAGE_GITSTATUS).equals(result); + } catch (UnsupportedEncodingException ex) { + throw new InternalError(ex.toString()); + } + } + static final String EXPECTED_USAGE_GITSTATUS = "Show the working tree status.%n" + + "Usage: git-status [<options>...] [--] [<pathspec>...]%n" + + "Displays paths that have differences between the index file and the current%n" + + "HEAD commit, paths that have differences between the working tree and the index%n" + + "file, and paths in the working tree that are not tracked by Git (and are not%n" + + "ignored by gitignore(5)). The first are what you would commit by running git%n" + + "commit; the second and third are what you could commit by running git add%n" + + "before running git commit.%n" + + " --ignored Show ignored files as well%n" + + " -b, --branch Show the branch and tracking info even in%n" + + " short-format%n" + + " -s, --short Give the output in the short-format%n" + + " -u, --untracked=<mode> Show untracked files.%n" + + " The mode parameter is optional (defaults to%n" + + " `all`), and is used to specify the handling of%n" + + " untracked files.%n" + + " The possible options are:%n" + + " * no - Show no untracked files.%n" + + " * normal - Shows untracked files and directories.%n" + + " * all - Also shows individual files in untracked%n" + + " directories.%n" + + " Default: all%n"; + + static final String EXPECTED_USAGE_GITSTATUS_ANSI = "Show the working tree status.%n" + + "Usage: @|bold git-status|@ [@|yellow <options>|@...] [--] [@|yellow <pathspec>|@...]%n" + + "Displays paths that have differences between the index file and the current%n" + + "HEAD commit, paths that have differences between the working tree and the index%n" + + "file, and paths in the working tree that are not tracked by Git (and are not%n" + + "ignored by gitignore(5)). The first are what you would commit by running git%n" + + "commit; the second and third are what you could commit by running git add%n" + + "before running git commit.%n" + + " @|yellow --ignored|@ Show ignored files as well%n" + + " @|yellow -b|@, @|yellow --branch|@ Show the branch and tracking info even in%n" + + " short-format%n" + + " @|yellow -s|@, @|yellow --short|@ Give the output in the short-format%n" + + " @|yellow -u|@, @|yellow --untracked|@=@|italic <mode>|@ Show untracked files.%n" + + " The mode parameter is optional (defaults to%n" + + " `all`), and is used to specify the handling of%n" + + " untracked files.%n" + + " The possible options are:%n" + + " * @|yellow no|@ - Show no untracked files.%n" + + " * @|yellow normal|@ - Shows untracked files and directories.%n" + + " * @|yellow all|@ - Also shows individual files in untracked%n" + + " directories.%n" + + " Default: all%n"; + + public void testUsageSubCommandCommit() { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + CommandLine.usage(new GitCommit(), new PrintStream(baos, true, "UTF8")); + String result = baos.toString("UTF8"); + System.out.println(result); + assert String.format(EXPECTED_USAGE_GITCOMMIT).equals(result); + } catch (UnsupportedEncodingException ex) { + throw new InternalError(ex.toString()); + } + } + static final String EXPECTED_USAGE_GITCOMMIT = "Usage:%n" + + "%n" + + "Record changes to the repository.%n" + + "%n" + + "git-commit [-ap] [--fixup=<commit>] [--squash=<commit>] [-c=<commit>]%n" + + " [-C=<commit>] [-F=<file>] [-m[=<msg>...]] [<files>...]%n" + + "%n" + + "Description:%n" + + "%n" + + "Stores the current contents of the index in a new commit along with a log%n" + + "message from the user describing the changes.%n" + + "%n" + + "Parameters:%n" + + " <files> the files to commit%n" + + "%n" + + "Options:%n" + + " -a, --all Tell the command to automatically stage files%n" + + " that have been modified and deleted, but new%n" + + " files you have not told Git about are not%n" + + " affected.%n" + + " -p, --patch Use the interactive patch selection interface to%n" + + " chose which changes to commit%n" + + " -C, --reuse-message=<commit>%n" + + " Take an existing commit object, and reuse the log%n" + + " message and the authorship information%n" + + " (including the timestamp) when creating the%n" + + " commit.%n" + + " -c, --reedit-message=<commit>%n" + + " Like -C, but with -c the editor is invoked, so%n" + + " that the user canfurther edit the commit%n" + + " message.%n" + + " --fixup=<commit> Construct a commit message for use with rebase%n" + + " --autosquash.%n" + + " --squash=<commit> Construct a commit message for use with rebase%n" + + " --autosquash. The commitmessage subject line is%n" + + " taken from the specified commit with a prefix%n" + + " of \"squash! \". Can be used with additional%n" + + " commit message options (-m/-c/-C/-F).%n" + + " -F, --file=<file> Take the commit message from the given file. Use%n" + + " - to read the message from the standard input.%n" + + " -m, --message[=<msg>...] Use the given <msg> as the commit message. If%n" + + " multiple -m options are given, their values are%n" + + " concatenated as separate paragraphs.%n"; + + static final String EXPECTED_USAGE_GITCOMMIT_ANSI = "@|bold,underline Usage:|@%n" + + "%n" + + "Record changes to the repository.%n" + + "%n" + + "@|bold git-commit|@ [@|yellow -ap|@] [@|yellow --fixup|@=@|italic <commit>|@] [@|yellow --squash|@=@|italic <commit>|@] [@|yellow -c|@=@|italic <commit>|@]%n" + + " [@|yellow -C|@=@|italic <commit>|@] [@|yellow -F|@=@|italic <file>|@] [@|yellow -m|@[=@|italic <msg>|@...]] [@|yellow <files>|@...]%n" + + "%n" + + "@|bold,underline Description:|@%n" + + "%n" + + "Stores the current contents of the index in a new commit along with a log%n" + + "message from the user describing the changes.%n" + + "%n" + + "@|bold,underline Parameters:|@%n" + + " @|yellow <files>|@ the files to commit%n" + + "%n" + + "@|bold,underline Options:|@%n" + + " @|yellow -a|@, @|yellow --all|@ Tell the command to automatically stage files%n" + + " that have been modified and deleted, but new%n" + + " files you have not told Git about are not%n" + + " affected.%n" + + " @|yellow -p|@, @|yellow --patch|@ Use the interactive patch selection interface to%n" + + " chose which changes to commit%n" + + " @|yellow -C|@, @|yellow --reuse-message|@=@|italic <commit|@@|italic >|@%n" + + " Take an existing commit object, and reuse the log%n" + + " message and the authorship information%n" + + " (including the timestamp) when creating the%n" + + " commit.%n" + + " @|yellow -c|@, @|yellow --reedit-message|@=@|italic <commi|@@|italic t>|@%n" + + " Like -C, but with -c the editor is invoked, so%n" + + " that the user canfurther edit the commit%n" + + " message.%n" + + " @|yellow --fixup|@=@|italic <commit>|@ Construct a commit message for use with rebase%n" + + " --autosquash.%n" + + " @|yellow --squash|@=@|italic <commit>|@ Construct a commit message for use with rebase%n" + + " --autosquash. The commitmessage subject line is%n" + + " taken from the specified commit with a prefix%n" + + " of \"squash! \". Can be used with additional%n" + + " commit message options (-m/-c/-C/-F).%n" + + " @|yellow -F|@, @|yellow --file|@=@|italic <file>|@ Take the commit message from the given file. Use%n" + + " - to read the message from the standard input.%n" + + " @|yellow -m|@, @|yellow --message|@[=@|italic <msg>|@...] Use the given <msg> as the commit message. If%n" + + " multiple -m options are given, their values are%n" + + " concatenated as separate paragraphs.%n"; +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c2818bec/src/changes/changes.xml ---------------------------------------------------------------------- diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 14837d7..d1b1e13 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -31,6 +31,9 @@ - "remove" - Removed --> <release version="2.9.0" date="2017-MM-DD" description="GA Release 2.9.0"> + <action issue="LOG4J2-2011" dev="rpopma" type="update"> + Replace JCommander command line parser with picocli to let users run Log4j2 utility applications without requiring an external dependency. + </action> <action issue="LOG4J2-2008" dev="rgoers" type="add"> Support printing multiple StructuredData elements in RFC5424Layout. </action>