You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rp...@apache.org on 2017/11/11 12:19:42 UTC

[2/2] logging-log4j2 git commit: LOG4J2-2088 Upgrade picocli to 2.0.3 from 0.9.8

LOG4J2-2088 Upgrade picocli to 2.0.3 from 0.9.8


Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/23bcd257
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/23bcd257
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/23bcd257

Branch: refs/heads/master
Commit: 23bcd257f7fa2617827c5210b0b1e074d42480cf
Parents: 7d52f13
Author: rpopma <rp...@apache.org>
Authored: Sat Nov 11 21:19:15 2017 +0900
Committer: rpopma <rp...@apache.org>
Committed: Sat Nov 11 21:19:15 2017 +0900

----------------------------------------------------------------------
 .../log4j/core/tools/picocli/CommandLine.java   | 241 +++--
 .../tools/picocli/CommandLineArityTest.java     | 934 ++++++++++++++++++
 .../core/tools/picocli/CommandLineHelpTest.java |  93 +-
 .../core/tools/picocli/CommandLineTest.java     | 963 ++-----------------
 .../logging/log4j/core/tools/picocli/Demo.java  |   3 +
 src/changes/changes.xml                         |   6 +-
 6 files changed, 1281 insertions(+), 959 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/23bcd257/log4j-core/src/main/java/org/apache/logging/log4j/core/tools/picocli/CommandLine.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/tools/picocli/CommandLine.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/tools/picocli/CommandLine.java
index ed6cec1..b8a19ab 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/tools/picocli/CommandLine.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/tools/picocli/CommandLine.java
@@ -55,7 +55,6 @@ import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Queue;
 import java.util.Set;
 import java.util.SortedSet;
@@ -133,7 +132,7 @@ import static org.apache.logging.log4j.core.tools.picocli.CommandLine.Help.Colum
  */
 public class CommandLine {
     /** This is picocli version {@value}. */
-    public static final String VERSION = "2.0.0";
+    public static final String VERSION = "2.0.3";
 
     private final Tracer tracer = new Tracer();
     private final Interpreter interpreter;
@@ -1791,10 +1790,10 @@ public class CommandLine {
         }
     }
     static void init(Class<?> cls,
-                     List<Field> requiredFields,
-                     Map<String, Field> optionName2Field,
-                     Map<Character, Field> singleCharOption2Field,
-                     List<Field> positionalParametersFields) {
+                              List<Field> requiredFields,
+                              Map<String, Field> optionName2Field,
+                              Map<Character, Field> singleCharOption2Field,
+                              List<Field> positionalParametersFields) {
         Field[] declaredFields = cls.getDeclaredFields();
         for (Field field : declaredFields) {
             field.setAccessible(true);
@@ -1863,6 +1862,7 @@ public class CommandLine {
 
         Interpreter(Object command) {
             converterRegistry.put(Path.class,          new BuiltIn.PathConverter());
+            converterRegistry.put(Object.class,        new BuiltIn.StringConverter());
             converterRegistry.put(String.class,        new BuiltIn.StringConverter());
             converterRegistry.put(StringBuilder.class, new BuiltIn.StringBuilderConverter());
             converterRegistry.put(CharSequence.class,  new BuiltIn.CharSequenceConverter());
@@ -2109,7 +2109,7 @@ public class CommandLine {
                 if (tracer.isDebug()) {tracer.debug("Position %d is in index range %s. Trying to assign args to %s, arity=%s%n", position, indexRange, positionalParam, arity);}
                 assertNoMissingParameters(positionalParam, arity.min, argsCopy);
                 int originalSize = argsCopy.size();
-                applyOption(positionalParam, Parameters.class, arity, false, argsCopy, initialized, "args[" + indexRange + "]");
+                applyOption(positionalParam, Parameters.class, arity, false, argsCopy, initialized, "args[" + indexRange + "] at position " + position);
                 int count = originalSize - argsCopy.size();
                 if (count > 0) { required.remove(positionalParam); }
                 consumed = Math.max(consumed, count);
@@ -2162,16 +2162,15 @@ public class CommandLine {
                     if (arity.min > 0 && !empty(cluster)) {
                         if (tracer.isDebug()) {tracer.debug("Trying to process '%s' as option parameter%n", cluster);}
                     }
-                    args.push(cluster); // interpret remainder as option parameter (CAUTION: may be empty string!)
                     // arity may be >= 1, or
                     // arity <= 0 && !cluster.startsWith(separator)
                     // e.g., boolean @Option("-v", arity=0, varargs=true); arg "-rvTRUE", remainder cluster="TRUE"
-                    if (!args.isEmpty() && args.peek().length() == 0 && !paramAttachedToOption) {
-                        args.pop(); // throw out empty string we get at the end of a group of clustered short options
+                    if (!empty(cluster)) {
+                        args.push(cluster); // interpret remainder as option parameter
                     }
                     int consumed = applyOption(field, Option.class, arity, paramAttachedToOption, args, initialized, argDescription);
                     // only return if cluster (and maybe more) was consumed, otherwise continue do-while loop
-                    if (consumed > 0) {
+                    if (empty(cluster) || consumed > 0 || args.isEmpty()) {
                         return;
                     }
                     cluster = args.pop();
@@ -2270,7 +2269,7 @@ public class CommandLine {
                 initialized.add(field);
             }
             if (tracer.level.isEnabled(level)) { level.print(tracer, traceMessage, field.getType().getSimpleName(),
-                    field.getDeclaringClass().getSimpleName(), field.getName(), String.valueOf(oldValue), String.valueOf(newValue), argDescription);
+                        field.getDeclaringClass().getSimpleName(), field.getName(), String.valueOf(oldValue), String.valueOf(newValue), argDescription);
             }
             field.set(command, newValue);
             return result;
@@ -2620,8 +2619,8 @@ public class CommandLine {
             return value == null
                     ? null
                     : (value.length() > 1 && value.startsWith("\"") && value.endsWith("\""))
-                    ? value.substring(1, value.length() - 1)
-                    : value;
+                        ? value.substring(1, value.length() - 1)
+                        : value;
         }
     }
     private static class PositionalParametersSorter implements Comparator<Field> {
@@ -3163,6 +3162,21 @@ public class CommandLine {
             return layout.toString();
         }
 
+        private static String heading(Ansi ansi, String values, Object... params) {
+            StringBuilder sb = join(ansi, new String[] {values}, new StringBuilder(), params);
+            String result = sb.toString();
+            result = result.endsWith(System.getProperty("line.separator"))
+                    ? result.substring(0, result.length() - System.getProperty("line.separator").length()) : result;
+            return result + new String(spaces(countTrailingSpaces(values)));
+        }
+        private static char[] spaces(int length) { char[] result = new char[length]; Arrays.fill(result, ' '); return result; }
+        private static int countTrailingSpaces(String str) {
+            if (str == null) {return 0;}
+            int trailingSpaces = 0;
+            for (int i = str.length() - 1; i >= 0 && str.charAt(i) == ' '; i--) { trailingSpaces++; }
+            return trailingSpaces;
+        }
+
         /** Formats each of the specified values and appends it to the specified StringBuilder.
          * @param ansi whether the result should contain ANSI escape codes or not
          * @param values the values to format and append to the StringBuilder
@@ -3174,12 +3188,16 @@ public class CommandLine {
                 TextTable table = new TextTable(ansi, usageHelpWidth);
                 table.indentWrappedLines = 0;
                 for (String summaryLine : values) {
-                    table.addRowValues(ansi.new Text(String.format(summaryLine, params)));
+                    Text[] lines = ansi.new Text(format(summaryLine, params)).splitLines();
+                    for (Text line : lines) {  table.addRowValues(line); }
                 }
                 table.toString(sb);
             }
             return sb;
         }
+        private static String format(String formatString,  Object... params) {
+            return formatString == null ? "" : String.format(formatString, params);
+        }
         /** Returns command custom synopsis as a string. A custom synopsis can be zero or more lines, and can be
          * specified declaratively with the {@link Command#customSynopsis()} annotation attribute or programmatically
          * by setting the Help instance's {@link Help#customSynopsis} field.
@@ -3221,14 +3239,14 @@ public class CommandLine {
          * @param params the parameters to use to format the header heading
          * @return the formatted header heading */
         public String headerHeading(Object... params) {
-            return ansi().new Text(format(headerHeading, params)).toString();
+            return heading(ansi(), headerHeading, params);
         }
 
         /** Returns the text displayed before the synopsis text; the result of {@code String.format(synopsisHeading, params)}.
          * @param params the parameters to use to format the synopsis heading
          * @return the formatted synopsis heading */
         public String synopsisHeading(Object... params) {
-            return ansi().new Text(format(synopsisHeading, params)).toString();
+            return heading(ansi(), synopsisHeading, params);
         }
 
         /** Returns the text displayed before the description text; an empty string if there is no description,
@@ -3236,7 +3254,7 @@ public class CommandLine {
          * @param params the parameters to use to format the description heading
          * @return the formatted description heading */
         public String descriptionHeading(Object... params) {
-            return empty(descriptionHeading) ? "" : ansi().new Text(format(descriptionHeading, params)).toString();
+            return empty(descriptionHeading) ? "" : heading(ansi(), descriptionHeading, params);
         }
 
         /** Returns the text displayed before the positional parameter list; an empty string if there are no positional
@@ -3244,7 +3262,7 @@ public class CommandLine {
          * @param params the parameters to use to format the parameter list heading
          * @return the formatted parameter list heading */
         public String parameterListHeading(Object... params) {
-            return positionalParametersFields.isEmpty() ? "" : ansi().new Text(format(parameterListHeading, params)).toString();
+            return positionalParametersFields.isEmpty() ? "" : heading(ansi(), parameterListHeading, params);
         }
 
         /** Returns the text displayed before the option list; an empty string if there are no options,
@@ -3252,7 +3270,7 @@ public class CommandLine {
          * @param params the parameters to use to format the option list heading
          * @return the formatted option list heading */
         public String optionListHeading(Object... params) {
-            return optionFields.isEmpty() ? "" : ansi().new Text(format(optionListHeading, params)).toString();
+            return optionFields.isEmpty() ? "" : heading(ansi(), optionListHeading, params);
         }
 
         /** Returns the text displayed before the command list; an empty string if there are no commands,
@@ -3260,17 +3278,14 @@ public class CommandLine {
          * @param params the parameters to use to format the command list heading
          * @return the formatted command list heading */
         public String commandListHeading(Object... params) {
-            return commands.isEmpty() ? "" : ansi().new Text(format(commandListHeading, params)).toString();
+            return commands.isEmpty() ? "" : heading(ansi(), commandListHeading, params);
         }
 
         /** Returns the text displayed before the footer text; the result of {@code String.format(footerHeading, params)}.
          * @param params the parameters to use to format the footer heading
          * @return the formatted footer heading */
         public String footerHeading(Object... params) {
-            return ansi().new Text(format(footerHeading, params)).toString();
-        }
-        private String format(String formatString,  Object[] params) {
-            return formatString == null ? "" : String.format(formatString, params);
+            return heading(ansi(), footerHeading, params);
         }
         /** Returns a 2-column list with command names and the first line of their header or (if absent) description.
          * @return a usage help section describing the added commands */
@@ -3441,27 +3456,25 @@ public class CommandLine {
         static class DefaultOptionRenderer implements IOptionRenderer {
             public String requiredMarker = " ";
             public Object command;
+            private String sep;
+            private boolean showDefault;
             public Text[][] render(Option option, Field field, IParamLabelRenderer paramLabelRenderer, ColorScheme scheme) {
                 String[] names = ShortestFirst.sort(option.names());
                 int shortOptionCount = names[0].length() == 2 ? 1 : 0;
                 String shortOption = shortOptionCount > 0 ? names[0] : "";
-                Text paramLabelText = paramLabelRenderer.renderParameterLabel(field, scheme.ansi(), scheme.optionParamStyles);
+                sep = shortOptionCount > 0 && names.length > 1 ? "," : "";
+
                 String longOption = join(names, shortOptionCount, names.length - shortOptionCount, ", ");
-                String sep = shortOptionCount > 0 && names.length > 1 ? "," : "";
+                Text longOptionText = createLongOptionText(field, paramLabelRenderer, scheme, longOption);
+
+                showDefault = command != null && !option.help() && !isBoolean(field.getType());
+                Object defaultValue = createDefaultValue(field);
 
-                // if no long option, fill in the space between the short option name and the param label value
-                if (paramLabelText.length > 0 && longOption.length() == 0) {
-                    sep = paramLabelRenderer.separator();
-                    // #181 paramLabelText may be =LABEL or [=LABEL...]
-                    int sepStart = paramLabelText.plainString().indexOf(sep);
-                    Text prefix = paramLabelText.substring(0, sepStart);
-                    paramLabelText = prefix.append(paramLabelText.substring(sepStart + sep.length()));
-                }
-                Text longOptionText = scheme.optionText(longOption);
-                longOptionText = longOptionText.append(paramLabelText);
                 String requiredOption = option.required() ? requiredMarker : "";
+                return renderDescriptionLines(option, scheme, requiredOption, shortOption, longOptionText, defaultValue);
+            }
 
-                boolean showDefault = command != null && !option.help() && !isBoolean(field.getType());
+            private Object createDefaultValue(Field field) {
                 Object defaultValue = null;
                 try {
                     defaultValue = field.get(command);
@@ -3476,22 +3489,57 @@ public class CommandLine {
                 } catch (Exception ex) {
                     showDefault = false;
                 }
-                final int descriptionCount = Math.max(1, option.description().length);
-                final int ROW_COUNT = showDefault ? descriptionCount + 1 : descriptionCount;
-                final int COLUMN_COUNT = 5;
+                return defaultValue;
+            }
+
+            private Text createLongOptionText(Field field, IParamLabelRenderer renderer, ColorScheme scheme, String longOption) {
+                Text paramLabelText = renderer.renderParameterLabel(field, scheme.ansi(), scheme.optionParamStyles);
+
+                // if no long option, fill in the space between the short option name and the param label value
+                if (paramLabelText.length > 0 && longOption.length() == 0) {
+                    sep = renderer.separator();
+                    // #181 paramLabelText may be =LABEL or [=LABEL...]
+                    int sepStart = paramLabelText.plainString().indexOf(sep);
+                    Text prefix = paramLabelText.substring(0, sepStart);
+                    paramLabelText = prefix.append(paramLabelText.substring(sepStart + sep.length()));
+                }
+                Text longOptionText = scheme.optionText(longOption);
+                longOptionText = longOptionText.append(paramLabelText);
+                return longOptionText;
+            }
+
+            private Text[][] renderDescriptionLines(Option option,
+                                                    ColorScheme scheme,
+                                                    String requiredOption,
+                                                    String shortOption,
+                                                    Text longOptionText,
+                                                    Object defaultValue) {
                 Text EMPTY = Ansi.EMPTY_TEXT;
-                Text[][] result = new Text[ROW_COUNT][COLUMN_COUNT];
-                result[0] = new Text[] { scheme.optionText(requiredOption), scheme.optionText(shortOption),
-                        scheme.ansi().new Text(sep), longOptionText, scheme.ansi().new Text(str(option.description(), 0)) };
+                List<Text[]> result = new ArrayList<Text[]>();
+                Text[] descriptionFirstLines = scheme.ansi().new Text(str(option.description(), 0)).splitLines();
+                if (descriptionFirstLines.length == 0) {
+                    if (showDefault) {
+                        descriptionFirstLines = new Text[]{scheme.ansi().new Text("  Default: " + defaultValue)};
+                        showDefault = false; // don't show the default value twice
+                    } else {
+                        descriptionFirstLines = new Text[]{ EMPTY };
+                    }
+                }
+                result.add(new Text[] { scheme.optionText(requiredOption), scheme.optionText(shortOption),
+                        scheme.ansi().new Text(sep), longOptionText, descriptionFirstLines[0] });
+                for (int i = 1; i < descriptionFirstLines.length; i++) {
+                    result.add(new Text[] { EMPTY, EMPTY, EMPTY, EMPTY, descriptionFirstLines[i] });
+                }
                 for (int i = 1; i < option.description().length; i++) {
-                    result[i] = new Text[] { EMPTY, EMPTY, EMPTY, EMPTY, scheme.ansi().new Text(option.description()[i]) };
+                    Text[] descriptionNextLines = scheme.ansi().new Text(option.description()[i]).splitLines();
+                    for (Text line : descriptionNextLines) {
+                        result.add(new Text[] { EMPTY, EMPTY, EMPTY, EMPTY, line });
+                    }
                 }
                 if (showDefault) {
-                    Arrays.fill(result[result.length - 1], EMPTY);
-                    int row = empty(result[ROW_COUNT - 2][COLUMN_COUNT - 1]) ? ROW_COUNT - 2 : ROW_COUNT - 1;
-                    result[row][COLUMN_COUNT - 1] = scheme.ansi().new Text("  Default: " + defaultValue);
+                    result.add(new Text[] { EMPTY, EMPTY, EMPTY, EMPTY, scheme.ansi().new Text("  Default: " + defaultValue) });
                 }
-                return result;
+                return result.toArray(new Text[result.size()][]);
             }
         }
         /** The MinimalOptionRenderer converts {@link Option Options} to a single row with two columns of text: an
@@ -3502,7 +3550,7 @@ public class CommandLine {
                 Text paramLabelText = parameterLabelRenderer.renderParameterLabel(field, scheme.ansi(), scheme.optionParamStyles);
                 optionText = optionText.append(paramLabelText);
                 return new Text[][] {{ optionText,
-                        scheme.ansi().new Text(option.description().length == 0 ? "" : option.description()[0]) }};
+                                        scheme.ansi().new Text(option.description().length == 0 ? "" : option.description()[0]) }};
             }
         }
         /** The MinimalParameterRenderer converts {@link Parameters Parameters} to a single row with two columns of
@@ -3546,14 +3594,21 @@ public class CommandLine {
                 Text label = paramLabelRenderer.renderParameterLabel(field, scheme.ansi(), scheme.parameterStyles);
                 Text requiredParameter = scheme.parameterText(Range.parameterArity(field).min > 0 ? requiredMarker : "");
 
-                final int COLUMN_COUNT = 5;
-                final Text EMPTY = Ansi.EMPTY_TEXT;
-                Text[][] result = new Text[Math.max(1, params.description().length)][COLUMN_COUNT];
-                result[0] = new Text[] { requiredParameter, EMPTY, EMPTY, label, scheme.ansi().new Text(str(params.description(), 0)) };
+                Text EMPTY = Ansi.EMPTY_TEXT;
+                List<Text[]> result = new ArrayList<Text[]>();
+                Text[] descriptionFirstLines = scheme.ansi().new Text(str(params.description(), 0)).splitLines();
+                if (descriptionFirstLines.length == 0) { descriptionFirstLines = new Text[]{ EMPTY }; }
+                result.add(new Text[] { requiredParameter, EMPTY, EMPTY, label, descriptionFirstLines[0] });
+                for (int i = 1; i < descriptionFirstLines.length; i++) {
+                    result.add(new Text[] { EMPTY, EMPTY, EMPTY, EMPTY, descriptionFirstLines[i] });
+                }
                 for (int i = 1; i < params.description().length; i++) {
-                    result[i] = new Text[] { EMPTY, EMPTY, EMPTY, EMPTY, scheme.ansi().new Text(params.description()[i]) };
+                    Text[] descriptionNextLines = scheme.ansi().new Text(params.description()[i]).splitLines();
+                    for (Text line : descriptionNextLines) {
+                        result.add(new Text[] { EMPTY, EMPTY, EMPTY, EMPTY, line });
+                    }
                 }
-                return result;
+                return result.toArray(new Text[result.size()][]);
             }
         }
         /** When customizing online usage help for an option parameter or a positional parameter, a custom
@@ -3830,12 +3885,12 @@ public class CommandLine {
             public TextTable(Ansi ansi) {
                 // "* -c, --create                Creates a ...."
                 this(ansi, new Column[] {
-                        new Column(2,                                        0, TRUNCATE), // "*"
-                        new Column(2,                                        0, TRUNCATE), // "-c"
-                        new Column(1,                                        0, TRUNCATE), // ","
-                        new Column(optionsColumnWidth - 2 - 2 - 1       , 1, SPAN),  // " --create"
-                        new Column(usageHelpWidth - optionsColumnWidth, 1, WRAP) // " Creates a ..."
-                });
+                            new Column(2,                                        0, TRUNCATE), // "*"
+                            new Column(2,                                        0, TRUNCATE), // "-c"
+                            new Column(1,                                        0, TRUNCATE), // ","
+                            new Column(optionsColumnWidth - 2 - 2 - 1       , 1, SPAN),  // " --create"
+                            new Column(usageHelpWidth - optionsColumnWidth, 1, WRAP) // " Creates a ..."
+                    });
             }
 
             /** Constructs a new TextTable with columns with the specified width, all SPANning  multiple columns on
@@ -3976,7 +4031,6 @@ public class CommandLine {
             private static int length(Text str) {
                 return str.length; // TODO count some characters as double length
             }
-            private char[] spaces(int length) { char[] result = new char[length]; Arrays.fill(result, ' '); return result; }
 
             private int copy(BreakIterator line, Text text, Text columnValue, int offset) {
                 // Deceive the BreakIterator to ensure no line breaks after '-' character
@@ -4211,7 +4265,7 @@ public class CommandLine {
                 public String on() { return CSI + startCode + "m"; }
                 public String off() { return CSI + endCode + "m"; }
 
-                /** Returns the concatenated ANSI escape codes for turning all specified styles on.
+				/** Returns the concatenated ANSI escape codes for turning all specified styles on.
                  * @param styles the styles to generate ANSI escape codes for
                  * @return the concatenated ANSI escape codes for turning all specified styles on */
                 public static String on(IStyle... styles) {
@@ -4221,7 +4275,7 @@ public class CommandLine {
                     }
                     return result.toString();
                 }
-                /** Returns the concatenated ANSI escape codes for turning all specified styles off.
+				/** Returns the concatenated ANSI escape codes for turning all specified styles off.
                  * @param styles the styles to generate ANSI escape codes for
                  * @return the concatenated ANSI escape codes for turning all specified styles off */
                 public static String off(IStyle... styles) {
@@ -4231,25 +4285,25 @@ public class CommandLine {
                     }
                     return result.toString();
                 }
-                /** Parses the specified style markup and returns the associated style.
-                 *  The markup may be one of the Style enum value names, or it may be one of the Style enum value
-                 *  names when {@code "fg_"} is prepended, or it may be one of the indexed colors in the 256 color palette.
+				/** Parses the specified style markup and returns the associated style.
+				 *  The markup may be one of the Style enum value names, or it may be one of the Style enum value
+				 *  names when {@code "fg_"} is prepended, or it may be one of the indexed colors in the 256 color palette.
                  * @param str the case-insensitive style markup to convert, e.g. {@code "blue"} or {@code "fg_blue"},
                  *          or {@code "46"} (indexed color) or {@code "0;5;0"} (RGB components of an indexed color)
-                 * @return the IStyle for the specified converter
-                 */
+				 * @return the IStyle for the specified converter
+				 */
                 public static IStyle fg(String str) {
                     try { return Style.valueOf(str.toLowerCase(ENGLISH)); } catch (Exception ignored) {}
                     try { return Style.valueOf("fg_" + str.toLowerCase(ENGLISH)); } catch (Exception ignored) {}
                     return new Palette256Color(true, str);
                 }
-                /** Parses the specified style markup and returns the associated style.
-                 *  The markup may be one of the Style enum value names, or it may be one of the Style enum value
-                 *  names when {@code "bg_"} is prepended, or it may be one of the indexed colors in the 256 color palette.
-                 * @param str the case-insensitive style markup to convert, e.g. {@code "blue"} or {@code "bg_blue"},
+				/** Parses the specified style markup and returns the associated style.
+				 *  The markup may be one of the Style enum value names, or it may be one of the Style enum value
+				 *  names when {@code "bg_"} is prepended, or it may be one of the indexed colors in the 256 color palette.
+				 * @param str the case-insensitive style markup to convert, e.g. {@code "blue"} or {@code "bg_blue"},
                  *          or {@code "46"} (indexed color) or {@code "0;5;0"} (RGB components of an indexed color)
-                 * @return the IStyle for the specified converter
-                 */
+				 * @return the IStyle for the specified converter
+				 */
                 public static IStyle bg(String str) {
                     try { return Style.valueOf(str.toLowerCase(ENGLISH)); } catch (Exception ignored) {}
                     try { return Style.valueOf("bg_" + str.toLowerCase(ENGLISH)); } catch (Exception ignored) {}
@@ -4404,6 +4458,27 @@ public class CommandLine {
                     try { return super.clone(); } catch (CloneNotSupportedException e) { throw new IllegalStateException(e); }
                 }
 
+                public Text[] splitLines() {
+                    List<Text> result = new ArrayList<Text>();
+                    boolean trailingEmptyString = false;
+                    int start = 0, end = 0;
+                    for (int i = 0; i < plain.length(); i++, end = i) {
+                        char c = plain.charAt(i);
+                        boolean eol = c == '\n';
+                        eol |= (c == '\r' && i + 1 < plain.length() && plain.charAt(i + 1) == '\n' && ++i > 0); // \r\n
+                        eol |= c == '\r';
+                        if (eol) {
+                            result.add(this.substring(start, end));
+                            trailingEmptyString = i == plain.length() - 1;
+                            start = i + 1;
+                        }
+                    }
+                    if (start < plain.length() || trailingEmptyString) {
+                        result.add(this.substring(start, plain.length()));
+                    }
+                    return result.toArray(new Text[result.size()]);
+                }
+
                 /** Returns a new {@code Text} instance that is a substring of this Text. Does not modify this instance!
                  * @param start index in the plain text where to start the substring
                  * @return a new Text instance that is a substring of this Text */
@@ -4536,7 +4611,7 @@ public class CommandLine {
             if (tracer.level.isEnabled(this)) { tracer.stream.printf(prefix(msg), params); }
         }
         private String prefix(String msg) { return "[picocli " + this + "] " + msg; }
-        static TraceLevel lookup(String key) { return key == null ? WARN : empty(key) ? INFO : valueOf(key); }
+        static TraceLevel lookup(String key) { return key == null ? WARN : empty(key) || "true".equalsIgnoreCase(key) ? INFO : valueOf(key); }
     }
     private static class Tracer {
         TraceLevel level = TraceLevel.lookup(System.getProperty("picocli.trace"));
@@ -4569,11 +4644,11 @@ public class CommandLine {
         private final CommandLine commandLine;
         public ExecutionException(CommandLine commandLine, String msg) {
             super(msg);
-            this.commandLine = Objects.requireNonNull(commandLine, "commandLine");
+            this.commandLine = Assert.notNull(commandLine, "commandLine");
         }
         public ExecutionException(CommandLine commandLine, String msg, Exception ex) {
             super(msg, ex);
-            this.commandLine = Objects.requireNonNull(commandLine, "commandLine");
+            this.commandLine = Assert.notNull(commandLine, "commandLine");
         }
         /** Returns the {@code CommandLine} object for the (sub)command that could not be invoked.
          * @return the {@code CommandLine} object for the (sub)command where invocation failed.
@@ -4597,7 +4672,7 @@ public class CommandLine {
          * @since 2.0 */
         public ParameterException(CommandLine commandLine, String msg) {
             super(msg);
-            this.commandLine = Objects.requireNonNull(commandLine, "commandLine");
+            this.commandLine = Assert.notNull(commandLine, "commandLine");
         }
         /** Constructs a new ParameterException with the specified CommandLine and error message.
          * @param commandLine the command or subcommand whose input was invalid
@@ -4606,7 +4681,7 @@ public class CommandLine {
          * @since 2.0 */
         public ParameterException(CommandLine commandLine, String msg, Exception ex) {
             super(msg, ex);
-            this.commandLine = Objects.requireNonNull(commandLine, "commandLine");
+            this.commandLine = Assert.notNull(commandLine, "commandLine");
         }
 
         /** Returns the {@code CommandLine} object for the (sub)command whose input could not be parsed.
@@ -4643,8 +4718,8 @@ public class CommandLine {
         }
         private static String describe(Field field, String separator) {
             String prefix = (field.isAnnotationPresent(Option.class))
-                    ? field.getAnnotation(Option.class).names()[0] + separator
-                    : "params[" + field.getAnnotation(Parameters.class).index() + "]" + separator;
+                ? field.getAnnotation(Option.class).names()[0] + separator
+                : "params[" + field.getAnnotation(Parameters.class).index() + "]" + separator;
             return prefix + Help.DefaultParamLabelRenderer.renderParameterName(field);
         }
     }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/23bcd257/log4j-core/src/test/java/org/apache/logging/log4j/core/tools/picocli/CommandLineArityTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/tools/picocli/CommandLineArityTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/tools/picocli/CommandLineArityTest.java
new file mode 100644
index 0000000..c9b435e
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/tools/picocli/CommandLineArityTest.java
@@ -0,0 +1,934 @@
+/*
+ * 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.tools.picocli;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.Socket;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.nio.charset.Charset;
+import java.sql.Time;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.UUID;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.apache.logging.log4j.core.tools.picocli.CommandLine.*;
+
+import static java.util.concurrent.TimeUnit.*;
+import static org.junit.Assert.*;
+import static org.apache.logging.log4j.core.tools.picocli.CommandLine.*;
+
+public class CommandLineArityTest {
+    @Before public void setUp() { System.clearProperty("picocli.trace"); }
+    @After public void tearDown() { System.clearProperty("picocli.trace"); }
+
+    private static void setTraceLevel(String level) {
+        System.setProperty("picocli.trace", level);
+    }
+    @Test
+    public void testArityConstructor_fixedRange() {
+        Range arity = new Range(1, 23, false, false, null);
+        assertEquals("min", 1, arity.min);
+        assertEquals("max", 23, arity.max);
+        assertEquals("1..23", arity.toString());
+        assertEquals(Range.valueOf("1..23"), arity);
+    }
+    @Test
+    public void testArityConstructor_variableRange() {
+        Range arity = new Range(1, Integer.MAX_VALUE, true, false, null);
+        assertEquals("min", 1, arity.min);
+        assertEquals("max", Integer.MAX_VALUE, arity.max);
+        assertEquals("1..*", arity.toString());
+        assertEquals(Range.valueOf("1..*"), arity);
+    }
+    @Test
+    public void testArityForOption_booleanFieldImplicitArity0() throws Exception {
+        Range arity = Range.optionArity(CommandLineTest.SupportedTypes.class.getDeclaredField("booleanField"));
+        assertEquals(Range.valueOf("0"), arity);
+        assertEquals("0", arity.toString());
+    }
+    @Test
+    public void testArityForOption_intFieldImplicitArity1() throws Exception {
+        Range arity = Range.optionArity(CommandLineTest.SupportedTypes.class.getDeclaredField("intField"));
+        assertEquals(Range.valueOf("1"), arity);
+        assertEquals("1", arity.toString());
+    }
+    @Test
+    public void testArityForOption_isExplicitlyDeclaredValue() throws Exception {
+        class Params {
+            @Option(names = "-timeUnitList", type = TimeUnit.class, arity = "3") List<TimeUnit> timeUnitList;
+        }
+        Range arity = Range.optionArity(Params.class.getDeclaredField("timeUnitList"));
+        assertEquals(Range.valueOf("3"), arity);
+        assertEquals("3", arity.toString());
+    }
+    @Test
+    public void testArityForOption_listFieldImplicitArity1() throws Exception {
+        class ImplicitList { @Option(names = "-a") List<Integer> listIntegers; }
+        Range arity = Range.optionArity(ImplicitList.class.getDeclaredField("listIntegers"));
+        assertEquals(Range.valueOf("1"), arity);
+        assertEquals("1", arity.toString());
+    }
+    @Test
+    public void testArityForOption_arrayFieldImplicitArity1() throws Exception {
+        class ImplicitList { @Option(names = "-a") int[] intArray; }
+        Range arity = Range.optionArity(ImplicitList.class.getDeclaredField("intArray"));
+        assertEquals(Range.valueOf("1"), arity);
+        assertEquals("1", arity.toString());
+    }
+    @Test
+    public void testArityForParameters_booleanFieldImplicitArity1() throws Exception {
+        class ImplicitBoolField { @Parameters boolean boolSingleValue; }
+        Range arity = Range.parameterArity(ImplicitBoolField.class.getDeclaredField("boolSingleValue"));
+        assertEquals(Range.valueOf("1"), arity);
+        assertEquals("1", arity.toString());
+    }
+    @Test
+    public void testArityForParameters_intFieldImplicitArity1() throws Exception {
+        class ImplicitSingleField { @Parameters int intSingleValue; }
+        Range arity = Range.parameterArity(ImplicitSingleField.class.getDeclaredField("intSingleValue"));
+        assertEquals(Range.valueOf("1"), arity);
+        assertEquals("1", arity.toString());
+    }
+    @Test
+    public void testArityForParameters_listFieldImplicitArity0_1() throws Exception {
+        class Params {
+            @Parameters(type = Integer.class) List<Integer> list;
+        }
+        Range arity = Range.parameterArity(Params.class.getDeclaredField("list"));
+        assertEquals(Range.valueOf("0..1"), arity);
+        assertEquals("0..1", arity.toString());
+    }
+    @Test
+    public void testArityForParameters_arrayFieldImplicitArity0_1() throws Exception {
+        class Args {
+            @Parameters File[] inputFiles;
+        }
+        Range arity = Range.parameterArity(Args.class.getDeclaredField("inputFiles"));
+        assertEquals(Range.valueOf("0..1"), arity);
+        assertEquals("0..1", arity.toString());
+    }
+    @Test
+    public void testArrayOptionsWithArity0_nConsumeAllArguments() {
+        final double[] DEFAULT_PARAMS = new double[] {1, 2};
+        class ArrayOptionsArity0_nAndParameters {
+            @Parameters double[] doubleParams = DEFAULT_PARAMS;
+            @Option(names = "-doubles", arity = "0..*") double[] doubleOptions;
+        }
+        ArrayOptionsArity0_nAndParameters
+                params = CommandLine.populateCommand(new ArrayOptionsArity0_nAndParameters(), "-doubles 1.1 2.2 3.3 4.4".split(" "));
+        assertArrayEquals(Arrays.toString(params.doubleOptions),
+                new double[] {1.1, 2.2, 3.3, 4.4}, params.doubleOptions, 0.000001);
+        assertArrayEquals(DEFAULT_PARAMS, params.doubleParams, 0.000001);
+    }
+
+    @Test
+    public void testArrayOptionsWithArity1_nConsumeAllArguments() {
+        class ArrayOptionsArity1_nAndParameters {
+            @Parameters double[] doubleParams;
+            @Option(names = "-doubles", arity = "1..*") double[] doubleOptions;
+        }
+        ArrayOptionsArity1_nAndParameters
+                params = CommandLine.populateCommand(new ArrayOptionsArity1_nAndParameters(), "-doubles 1.1 2.2 3.3 4.4".split(" "));
+        assertArrayEquals(Arrays.toString(params.doubleOptions),
+                new double[] {1.1, 2.2, 3.3, 4.4}, params.doubleOptions, 0.000001);
+        assertArrayEquals(null, params.doubleParams, 0.000001);
+    }
+
+    @Test
+    public void testArrayOptionsWithArity2_nConsumeAllArguments() {
+        class ArrayOptionsArity2_nAndParameters {
+            @Parameters double[] doubleParams;
+            @Option(names = "-doubles", arity = "2..*") double[] doubleOptions;
+        }
+        ArrayOptionsArity2_nAndParameters
+                params = CommandLine.populateCommand(new ArrayOptionsArity2_nAndParameters(), "-doubles 1.1 2.2 3.3 4.4".split(" "));
+        assertArrayEquals(Arrays.toString(params.doubleOptions),
+                new double[] {1.1, 2.2, 3.3, 4.4}, params.doubleOptions, 0.000001);
+        assertArrayEquals(null, params.doubleParams, 0.000001);
+    }
+
+    @Test
+    public void testArrayOptionArity2_nConsumesAllArgumentsUpToClusteredOption() {
+        class ArrayOptionsArity2_nAndParameters {
+            @Parameters String[] stringParams;
+            @Option(names = "-s", arity = "2..*") String[] stringOptions;
+            @Option(names = "-v") boolean verbose;
+            @Option(names = "-f") File file;
+        }
+        ArrayOptionsArity2_nAndParameters
+                params = CommandLine.populateCommand(new ArrayOptionsArity2_nAndParameters(), "-s 1.1 2.2 3.3 4.4 -vfFILE 5.5".split(" "));
+        assertArrayEquals(Arrays.toString(params.stringOptions),
+                new String[] {"1.1", "2.2", "3.3", "4.4"}, params.stringOptions);
+        assertTrue(params.verbose);
+        assertEquals(new File("FILE"), params.file);
+        assertArrayEquals(new String[] {"5.5"}, params.stringParams);
+    }
+
+    @Test
+    public void testArrayOptionArity2_nConsumesAllArgumentIncludingQuotedSimpleOption() {
+        class ArrayOptionArity2_nAndParameters {
+            @Parameters String[] stringParams;
+            @Option(names = "-s", arity = "2..*") String[] stringOptions;
+            @Option(names = "-v") boolean verbose;
+            @Option(names = "-f") File file;
+        }
+        ArrayOptionArity2_nAndParameters
+                params = CommandLine.populateCommand(new ArrayOptionArity2_nAndParameters(), "-s 1.1 2.2 3.3 4.4 \"-v\" \"-f\" \"FILE\" 5.5".split(" "));
+        assertArrayEquals(Arrays.toString(params.stringOptions),
+                new String[] {"1.1", "2.2", "3.3", "4.4", "-v", "-f", "FILE", "5.5"}, params.stringOptions);
+        assertFalse("verbose", params.verbose);
+        assertNull("file", params.file);
+        assertArrayEquals(null, params.stringParams);
+    }
+
+    @Test
+    public void testArrayOptionArity2_nConsumesAllArgumentIncludingQuotedClusteredOption() {
+        class ArrayOptionArity2_nAndParameters {
+            @Parameters String[] stringParams;
+            @Option(names = "-s", arity = "2..*") String[] stringOptions;
+            @Option(names = "-v") boolean verbose;
+            @Option(names = "-f") File file;
+        }
+        ArrayOptionArity2_nAndParameters
+                params = CommandLine.populateCommand(new ArrayOptionArity2_nAndParameters(), "-s 1.1 2.2 3.3 4.4 \"-vfFILE\" 5.5".split(" "));
+        assertArrayEquals(Arrays.toString(params.stringOptions),
+                new String[] {"1.1", "2.2", "3.3", "4.4", "-vfFILE", "5.5"}, params.stringOptions);
+        assertFalse("verbose", params.verbose);
+        assertNull("file", params.file);
+        assertArrayEquals(null, params.stringParams);
+    }
+
+    @Test
+    public void testArrayOptionArity2_nConsumesAllArgumentsUpToNextSimpleOption() {
+        class ArrayOptionArity2_nAndParameters {
+            @Parameters double[] doubleParams;
+            @Option(names = "-s", arity = "2..*") String[] stringOptions;
+            @Option(names = "-v") boolean verbose;
+            @Option(names = "-f") File file;
+        }
+        ArrayOptionArity2_nAndParameters
+                params = CommandLine.populateCommand(new ArrayOptionArity2_nAndParameters(), "-s 1.1 2.2 3.3 4.4 -v -f=FILE 5.5".split(" "));
+        assertArrayEquals(Arrays.toString(params.stringOptions),
+                new String[] {"1.1", "2.2", "3.3", "4.4"}, params.stringOptions);
+        assertTrue(params.verbose);
+        assertEquals(new File("FILE"), params.file);
+        assertArrayEquals(new double[] {5.5}, params.doubleParams, 0.000001);
+    }
+
+    @Test
+    public void testArrayOptionArity2_nConsumesAllArgumentsUpToNextOptionWithAttachment() {
+        class ArrayOptionArity2_nAndParameters {
+            @Parameters double[] doubleParams;
+            @Option(names = "-s", arity = "2..*") String[] stringOptions;
+            @Option(names = "-v") boolean verbose;
+            @Option(names = "-f") File file;
+        }
+        ArrayOptionArity2_nAndParameters
+                params = CommandLine.populateCommand(new ArrayOptionArity2_nAndParameters(), "-s 1.1 2.2 3.3 4.4 -f=FILE -v 5.5".split(" "));
+        assertArrayEquals(Arrays.toString(params.stringOptions),
+                new String[] {"1.1", "2.2", "3.3", "4.4"}, params.stringOptions);
+        assertTrue(params.verbose);
+        assertEquals(new File("FILE"), params.file);
+        assertArrayEquals(new double[] {5.5}, params.doubleParams, 0.000001);
+    }
+
+    @Test
+    public void testArrayOptionArityNConsumeAllArguments() {
+        class ArrayOptionArityNAndParameters {
+            @Parameters char[] charParams;
+            @Option(names = "-chars", arity = "*") char[] charOptions;
+        }
+        ArrayOptionArityNAndParameters
+                params = CommandLine.populateCommand(new ArrayOptionArityNAndParameters(), "-chars a b c d".split(" "));
+        assertArrayEquals(Arrays.toString(params.charOptions),
+                new char[] {'a', 'b', 'c', 'd'}, params.charOptions);
+        assertArrayEquals(null, params.charParams);
+    }
+    @Test
+    public void testMissingRequiredParams() {
+        class Example {
+            @Parameters(index = "1", arity = "0..1") String optional;
+            @Parameters(index = "0") String mandatory;
+        }
+        try { CommandLine.populateCommand(new Example(), new String[] {"mandatory"}); }
+        catch (MissingParameterException ex) { fail(); }
+
+        try {
+            CommandLine.populateCommand(new Example(), new String[0]);
+            fail("Should not accept missing mandatory parameter");
+        } catch (MissingParameterException ex) {
+            assertEquals("Missing required parameter: <mandatory>", ex.getMessage());
+        }
+    }
+    @Test
+    public void testMissingRequiredParams1() {
+        class Tricky1 {
+            @Parameters(index = "2") String anotherMandatory;
+            @Parameters(index = "1", arity = "0..1") String optional;
+            @Parameters(index = "0") String mandatory;
+        }
+        try {
+            CommandLine.populateCommand(new Tricky1(), new String[0]);
+            fail("Should not accept missing mandatory parameter");
+        } catch (MissingParameterException ex) {
+            assertEquals("Missing required parameters: <mandatory>, <anotherMandatory>", ex.getMessage());
+        }
+        try {
+            CommandLine.populateCommand(new Tricky1(), new String[] {"firstonly"});
+            fail("Should not accept missing mandatory parameter");
+        } catch (MissingParameterException ex) {
+            assertEquals("Missing required parameter: <anotherMandatory>", ex.getMessage());
+        }
+    }
+    @Test
+    public void testMissingRequiredParams2() {
+        class Tricky2 {
+            @Parameters(index = "2", arity = "0..1") String anotherOptional;
+            @Parameters(index = "1", arity = "0..1") String optional;
+            @Parameters(index = "0") String mandatory;
+        }
+        try { CommandLine.populateCommand(new Tricky2(), new String[] {"mandatory"}); }
+        catch (MissingParameterException ex) { fail(); }
+
+        try {
+            CommandLine.populateCommand(new Tricky2(), new String[0]);
+            fail("Should not accept missing mandatory parameter");
+        } catch (MissingParameterException ex) {
+            assertEquals("Missing required parameter: <mandatory>", ex.getMessage());
+        }
+    }
+    @Test
+    public void testMissingRequiredParamsWithOptions() {
+        class Tricky3 {
+            @Option(names="-v") boolean more;
+            @Option(names="-t") boolean any;
+            @Parameters(index = "1") String alsoMandatory;
+            @Parameters(index = "0") String mandatory;
+        }
+        try {
+            CommandLine.populateCommand(new Tricky3(), new String[] {"-t", "-v", "mandatory"});
+            fail("Should not accept missing mandatory parameter");
+        } catch (MissingParameterException ex) {
+            assertEquals("Missing required parameter: <alsoMandatory>", ex.getMessage());
+        }
+
+        try {
+            CommandLine.populateCommand(new Tricky3(), new String[] { "-t", "-v"});
+            fail("Should not accept missing two mandatory parameters");
+        } catch (MissingParameterException ex) {
+            assertEquals("Missing required parameters: <mandatory>, <alsoMandatory>", ex.getMessage());
+        }
+    }
+    @Test
+    public void testMissingRequiredParamWithOption() {
+        class Tricky3 {
+            @Option(names="-t") boolean any;
+            @Parameters(index = "0") String mandatory;
+        }
+        try {
+            CommandLine.populateCommand(new Tricky3(), new String[] {"-t"});
+            fail("Should not accept missing mandatory parameter");
+        } catch (MissingParameterException ex) {
+            assertEquals("Missing required parameter: <mandatory>", ex.getMessage());
+        }
+    }
+    @Test
+    public void testNoMissingRequiredParamErrorIfHelpOptionSpecified() {
+        class App {
+            @Parameters(hidden = true)  // "hidden": don't show this parameter in usage help message
+                    List<String> allParameters; // no "index" attribute: captures _all_ arguments (as Strings)
+
+            @Parameters(index = "0")    InetAddress  host;
+            @Parameters(index = "1")    int          port;
+            @Parameters(index = "2..*") File[]       files;
+
+            @Option(names = "-?", help = true) boolean help;
+        }
+        CommandLine.populateCommand(new App(), new String[] {"-?"});
+        try {
+            CommandLine.populateCommand(new App(), new String[0]);
+            fail("Should not accept missing mandatory parameter");
+        } catch (MissingParameterException ex) {
+            assertEquals("Missing required parameters: <host>, <port>", ex.getMessage());
+        }
+    }
+    @Test
+    public void testNoMissingRequiredParamErrorWithLabelIfHelpOptionSpecified() {
+        class App {
+            @Parameters(hidden = true)  // "hidden": don't show this parameter in usage help message
+                    List<String> allParameters; // no "index" attribute: captures _all_ arguments (as Strings)
+
+            @Parameters(index = "0", paramLabel = "HOST")     InetAddress  host;
+            @Parameters(index = "1", paramLabel = "PORT")     int          port;
+            @Parameters(index = "2..*", paramLabel = "FILES") File[]       files;
+
+            @Option(names = "-?", help = true) boolean help;
+        }
+        CommandLine.populateCommand(new App(), new String[] {"-?"});
+        try {
+            CommandLine.populateCommand(new App(), new String[0]);
+            fail("Should not accept missing mandatory parameter");
+        } catch (MissingParameterException ex) {
+            assertEquals("Missing required parameters: HOST, PORT", ex.getMessage());
+        }
+    }
+
+    private static class BooleanOptionsArity0_nAndParameters {
+        @Parameters String[] params;
+        @Option(names = "-bool", arity = "0..*") boolean bool;
+        @Option(names = {"-v", "-other"}, arity="0..*") boolean vOrOther;
+        @Option(names = "-r") boolean rBoolean;
+    }
+    @Test
+    public void testBooleanOptionsArity0_nConsume1ArgumentIfPossible() { // ignores varargs
+        BooleanOptionsArity0_nAndParameters
+                params = CommandLine.populateCommand(new BooleanOptionsArity0_nAndParameters(), "-bool false false true".split(" "));
+        assertFalse(params.bool);
+        assertArrayEquals(new String[]{ "false", "true"}, params.params);
+    }
+    @Test
+    public void testBooleanOptionsArity0_nRequiresNoArgument() { // ignores varargs
+        BooleanOptionsArity0_nAndParameters
+                params = CommandLine.populateCommand(new BooleanOptionsArity0_nAndParameters(), "-bool".split(" "));
+        assertTrue(params.bool);
+    }
+    @Test
+    public void testBooleanOptionsArity0_nConsume0ArgumentsIfNextArgIsOption() { // ignores varargs
+        BooleanOptionsArity0_nAndParameters
+                params = CommandLine.populateCommand(new BooleanOptionsArity0_nAndParameters(), "-bool -other".split(" "));
+        assertTrue(params.bool);
+        assertTrue(params.vOrOther);
+    }
+    @Test
+    public void testBooleanOptionsArity0_nConsume0ArgumentsIfNextArgIsParameter() { // ignores varargs
+        BooleanOptionsArity0_nAndParameters
+                params = CommandLine.populateCommand(new BooleanOptionsArity0_nAndParameters(), "-bool 123 -other".split(" "));
+        assertTrue(params.bool);
+        assertTrue(params.vOrOther);
+        assertArrayEquals(new String[]{ "123"}, params.params);
+    }
+    @Test
+    public void testBooleanOptionsArity0_nFailsIfAttachedParamNotABoolean() { // ignores varargs
+        try {
+            CommandLine.populateCommand(new BooleanOptionsArity0_nAndParameters(), "-bool=123 -other".split(" "));
+            fail("was able to assign 123 to boolean");
+        } catch (CommandLine.ParameterException ex) {
+            assertEquals("'123' is not a boolean for option '-bool'", ex.getMessage());
+        }
+    }
+    @Test
+    public void testBooleanOptionsArity0_nShortFormFailsIfAttachedParamNotABoolean() { // ignores varargs
+        try {
+            CommandLine.populateCommand(new BooleanOptionsArity0_nAndParameters(), "-rv234 -bool".split(" "));
+            fail("Expected exception");
+        } catch (UnmatchedArgumentException ok) {
+            assertEquals("Unmatched argument [-234]", ok.getMessage());
+        }
+    }
+    @Test
+    public void testBooleanOptionsArity0_nShortFormFailsIfAttachedParamNotABooleanWithUnmatchedArgsAllowed() { // ignores varargs
+        setTraceLevel("OFF");
+        CommandLine cmd = new CommandLine(new BooleanOptionsArity0_nAndParameters()).setUnmatchedArgumentsAllowed(true);
+        cmd.parse("-rv234 -bool".split(" "));
+        assertEquals(Arrays.asList("-234"), cmd.getUnmatchedArguments());
+    }
+    @Test
+    public void testBooleanOptionsArity0_nShortFormFailsIfAttachedWithSepParamNotABoolean() { // ignores varargs
+        try {
+            CommandLine.populateCommand(new BooleanOptionsArity0_nAndParameters(), "-rv=234 -bool".split(" "));
+            fail("was able to assign 234 to boolean");
+        } catch (CommandLine.ParameterException ex) {
+            assertEquals("'234' is not a boolean for option '-v'", ex.getMessage());
+        }
+    }
+
+    private static class BooleanOptionsArity1_nAndParameters {
+        @Parameters boolean[] boolParams;
+        @Option(names = "-bool", arity = "1..*") boolean aBoolean;
+    }
+    @Test
+    public void testBooleanOptionsArity1_nConsume1Argument() { // ignores varargs
+        BooleanOptionsArity1_nAndParameters
+                params = CommandLine.populateCommand(new BooleanOptionsArity1_nAndParameters(), "-bool false false true".split(" "));
+        assertFalse(params.aBoolean);
+        assertArrayEquals(new boolean[]{ false, true}, params.boolParams);
+
+        params = CommandLine.populateCommand(new BooleanOptionsArity1_nAndParameters(), "-bool true false true".split(" "));
+        assertTrue(params.aBoolean);
+        assertArrayEquals(new boolean[]{ false, true}, params.boolParams);
+    }
+    @Test
+    public void testBooleanOptionsArity1_nCaseInsensitive() { // ignores varargs
+        BooleanOptionsArity1_nAndParameters
+                params = CommandLine.populateCommand(new BooleanOptionsArity1_nAndParameters(), "-bool fAlsE false true".split(" "));
+        assertFalse(params.aBoolean);
+        assertArrayEquals(new boolean[]{ false, true}, params.boolParams);
+
+        params = CommandLine.populateCommand(new BooleanOptionsArity1_nAndParameters(), "-bool FaLsE false true".split(" "));
+        assertFalse(params.aBoolean);
+        assertArrayEquals(new boolean[]{ false, true}, params.boolParams);
+
+        params = CommandLine.populateCommand(new BooleanOptionsArity1_nAndParameters(), "-bool tRuE false true".split(" "));
+        assertTrue(params.aBoolean);
+        assertArrayEquals(new boolean[]{ false, true}, params.boolParams);
+    }
+    @Test
+    public void testBooleanOptionsArity1_nErrorIfValueNotTrueOrFalse() { // ignores varargs
+        try {
+            CommandLine.populateCommand(new BooleanOptionsArity1_nAndParameters(), "-bool abc".split(" "));
+            fail("Invalid format abc was accepted for boolean");
+        } catch (CommandLine.ParameterException expected) {
+            assertEquals("'abc' is not a boolean for option '-bool'", expected.getMessage());
+        }
+    }
+    @Test
+    public void testBooleanOptionsArity1_nErrorIfValueMissing() {
+        try {
+            CommandLine.populateCommand(new BooleanOptionsArity1_nAndParameters(), "-bool".split(" "));
+            fail("Missing param was accepted for boolean with arity=1");
+        } catch (CommandLine.ParameterException expected) {
+            assertEquals("Missing required parameter for option '-bool' at index 0 (<aBoolean>)", expected.getMessage());
+        }
+    }
+
+    @Test
+    public void testBooleanOptionArity0Consumes0Arguments() {
+        class BooleanOptionArity0AndParameters {
+            @Parameters boolean[] boolParams;
+            @Option(names = "-bool", arity = "0") boolean aBoolean;
+        }
+        BooleanOptionArity0AndParameters
+                params = CommandLine.populateCommand(new BooleanOptionArity0AndParameters(), "-bool true false true".split(" "));
+        assertTrue(params.aBoolean);
+        assertArrayEquals(new boolean[]{true, false, true}, params.boolParams);
+    }
+    @Test(expected = MissingParameterException.class)
+    public void testSingleValueFieldDefaultMinArityIs1() {
+        CommandLine.populateCommand(new CommandLineTest.SupportedTypes(),  "-Long");
+    }
+    @Test
+    public void testSingleValueFieldDefaultMinArityIsOne() {
+        try {
+            CommandLine.populateCommand(new CommandLineTest.SupportedTypes(),  "-Long", "-boolean");
+            fail("should fail");
+        } catch (CommandLine.ParameterException ex) {
+            assertEquals("Could not convert '-boolean' to Long for option '-Long'" +
+                    ": java.lang.NumberFormatException: For input string: \"-boolean\"", ex.getMessage());
+        }
+    }
+
+    @Test
+    public void testIntOptionArity1_nConsumes1Argument() { // ignores varargs
+        class IntOptionArity1_nAndParameters {
+            @Parameters int[] intParams;
+            @Option(names = "-int", arity = "1..*") int anInt;
+        }
+        IntOptionArity1_nAndParameters
+                params = CommandLine.populateCommand(new IntOptionArity1_nAndParameters(), "-int 23 42 7".split(" "));
+        assertEquals(23, params.anInt);
+        assertArrayEquals(new int[]{ 42, 7}, params.intParams);
+    }
+
+    @Test
+    public void testArrayOptionsWithArity0Consume0Arguments() {
+        class OptionsArray0ArityAndParameters {
+            @Parameters double[] doubleParams;
+            @Option(names = "-doubles", arity = "0") double[] doubleOptions;
+        }
+        OptionsArray0ArityAndParameters
+                params = CommandLine.populateCommand(new OptionsArray0ArityAndParameters(), "-doubles 1.1 2.2 3.3 4.4".split(" "));
+        assertArrayEquals(Arrays.toString(params.doubleOptions),
+                new double[0], params.doubleOptions, 0.000001);
+        assertArrayEquals(new double[]{1.1, 2.2, 3.3, 4.4}, params.doubleParams, 0.000001);
+    }
+
+    @Test
+    public void testArrayOptionWithArity1Consumes1Argument() {
+        class Options1ArityAndParameters {
+            @Parameters double[] doubleParams;
+            @Option(names = "-doubles", arity = "1") double[] doubleOptions;
+        }
+        Options1ArityAndParameters
+                params = CommandLine.populateCommand(new Options1ArityAndParameters(), "-doubles 1.1 2.2 3.3 4.4".split(" "));
+        assertArrayEquals(Arrays.toString(params.doubleOptions),
+                new double[] {1.1}, params.doubleOptions, 0.000001);
+        assertArrayEquals(new double[]{2.2, 3.3, 4.4}, params.doubleParams, 0.000001);
+
+        // repeated occurrence
+        params = CommandLine.populateCommand(new Options1ArityAndParameters(), "-doubles 1.1 -doubles 2.2 -doubles 3.3 4.4".split(" "));
+        assertArrayEquals(Arrays.toString(params.doubleOptions),
+                new double[] {1.1, 2.2, 3.3}, params.doubleOptions, 0.000001);
+        assertArrayEquals(new double[]{4.4}, params.doubleParams, 0.000001);
+
+    }
+
+    private static class ArrayOptionArity2AndParameters {
+        @Parameters double[] doubleParams;
+        @Option(names = "-doubles", arity = "2") double[] doubleOptions;
+    }
+    @Test
+    public void testArrayOptionWithArity2Consumes2Arguments() {
+        ArrayOptionArity2AndParameters
+                params = CommandLine.populateCommand(new ArrayOptionArity2AndParameters(), "-doubles 1.1 2.2 3.3 4.4".split(" "));
+        assertArrayEquals(Arrays.toString(params.doubleOptions),
+                new double[] {1.1, 2.2, }, params.doubleOptions, 0.000001);
+        assertArrayEquals(new double[]{3.3, 4.4}, params.doubleParams, 0.000001);
+
+        // repeated occurrence
+        params = CommandLine.populateCommand(new ArrayOptionArity2AndParameters(), "-doubles 1.1 2.2 -doubles 3.3 4.4 0".split(" "));
+        assertArrayEquals(Arrays.toString(params.doubleOptions),
+                new double[] {1.1, 2.2, 3.3, 4.4 }, params.doubleOptions, 0.000001);
+        assertArrayEquals(new double[]{ 0.0 }, params.doubleParams, 0.000001);
+    }
+    @Test
+    public void testArrayOptionsWithArity2Consume2ArgumentsEvenIfFirstIsAttached() {
+        ArrayOptionArity2AndParameters
+                params = CommandLine.populateCommand(new ArrayOptionArity2AndParameters(), "-doubles=1.1 2.2 3.3 4.4".split(" "));
+        assertArrayEquals(Arrays.toString(params.doubleOptions),
+                new double[] {1.1, 2.2, }, params.doubleOptions, 0.000001);
+        assertArrayEquals(new double[]{3.3, 4.4}, params.doubleParams, 0.000001);
+
+        // repeated occurrence
+        params = CommandLine.populateCommand(new ArrayOptionArity2AndParameters(), "-doubles=1.1 2.2 -doubles=3.3 4.4 0".split(" "));
+        assertArrayEquals(Arrays.toString(params.doubleOptions),
+                new double[] {1.1, 2.2, 3.3, 4.4}, params.doubleOptions, 0.000001);
+        assertArrayEquals(new double[]{0}, params.doubleParams, 0.000001);
+    }
+    /** Arity should not limit the total number of values put in an array or collection #191 */
+    @Test
+    public void testArrayOptionsWithArity2MayContainMoreThan2Values() {
+        ArrayOptionArity2AndParameters
+                params = CommandLine.populateCommand(new ArrayOptionArity2AndParameters(), "-doubles=1 2 -doubles 3 4 -doubles 5 6".split(" "));
+        assertArrayEquals(Arrays.toString(params.doubleOptions),
+                new double[] {1, 2, 3, 4, 5, 6 }, params.doubleOptions, 0.000001);
+        assertArrayEquals(null, params.doubleParams, 0.000001);
+    }
+
+    @Test
+    public void testArrayOptionWithoutArityConsumesOneArgument() { // #192
+        class OptionsNoArityAndParameters {
+            @Parameters char[] charParams;
+            @Option(names = "-chars") char[] charOptions;
+        }
+        OptionsNoArityAndParameters
+                params = CommandLine.populateCommand(new OptionsNoArityAndParameters(), "-chars a b c d".split(" "));
+        assertArrayEquals(Arrays.toString(params.charOptions),
+                new char[] {'a', }, params.charOptions);
+        assertArrayEquals(Arrays.toString(params.charParams), new char[] {'b', 'c', 'd'}, params.charParams);
+
+        // repeated occurrence
+        params = CommandLine.populateCommand(new OptionsNoArityAndParameters(), "-chars a -chars b c d".split(" "));
+        assertArrayEquals(Arrays.toString(params.charOptions),
+                new char[] {'a', 'b', }, params.charOptions);
+        assertArrayEquals(Arrays.toString(params.charParams), new char[] {'c', 'd'}, params.charParams);
+
+        try {
+            CommandLine.populateCommand(new OptionsNoArityAndParameters(), "-chars".split(" "));
+            fail("expected MissingParameterException");
+        } catch (MissingParameterException ok) {
+            assertEquals("Missing required parameter for option '-chars' (<charOptions>)", ok.getMessage());
+        }
+    }
+
+    @Test
+    public void testArrayParametersWithDefaultArity() {
+        class ArrayParamsDefaultArity {
+            @Parameters
+            List<String> params;
+        }
+        ArrayParamsDefaultArity params = CommandLine.populateCommand(new ArrayParamsDefaultArity(), "a", "b", "c");
+        assertEquals(Arrays.asList("a", "b", "c"), params.params);
+
+        params = CommandLine.populateCommand(new ArrayParamsDefaultArity(), "a");
+        assertEquals(Arrays.asList("a"), params.params);
+
+        params = CommandLine.populateCommand(new ArrayParamsDefaultArity());
+        assertEquals(null, params.params);
+    }
+
+    @Test
+    public void testArrayParametersWithArityMinusOneToN() {
+        class ArrayParamsNegativeArity {
+            @Parameters(arity = "-1..*")
+            List<String> params;
+        }
+        ArrayParamsNegativeArity params = CommandLine.populateCommand(new ArrayParamsNegativeArity(), "a", "b", "c");
+        assertEquals(Arrays.asList("a", "b", "c"), params.params);
+
+        params = CommandLine.populateCommand(new ArrayParamsNegativeArity(), "a");
+        assertEquals(Arrays.asList("a"), params.params);
+
+        params = CommandLine.populateCommand(new ArrayParamsNegativeArity());
+        assertEquals(null, params.params);
+    }
+
+    @Test
+    public void testArrayParametersArity0_n() {
+        class ArrayParamsArity0_n {
+            @Parameters(arity = "0..*")
+            List<String> params;
+        }
+        ArrayParamsArity0_n params = CommandLine.populateCommand(new ArrayParamsArity0_n(), "a", "b", "c");
+        assertEquals(Arrays.asList("a", "b", "c"), params.params);
+
+        params = CommandLine.populateCommand(new ArrayParamsArity0_n(), "a");
+        assertEquals(Arrays.asList("a"), params.params);
+
+        params = CommandLine.populateCommand(new ArrayParamsArity0_n());
+        assertEquals(null, params.params);
+    }
+
+    @Test
+    public void testArrayParametersArity1_n() {
+        class ArrayParamsArity1_n {
+            @Parameters(arity = "1..*")
+            List<String> params;
+        }
+        ArrayParamsArity1_n params = CommandLine.populateCommand(new ArrayParamsArity1_n(), "a", "b", "c");
+        assertEquals(Arrays.asList("a", "b", "c"), params.params);
+
+        params = CommandLine.populateCommand(new ArrayParamsArity1_n(), "a");
+        assertEquals(Arrays.asList("a"), params.params);
+
+        try {
+            params = CommandLine.populateCommand(new ArrayParamsArity1_n());
+            fail("Should not accept input with missing parameter");
+        } catch (MissingParameterException ex) {
+            assertEquals("Missing required parameters at positions 0..*: <params>", ex.getMessage());
+        }
+    }
+
+    @Test
+    public void testArrayParametersArity2_n() {
+        class ArrayParamsArity2_n {
+            @Parameters(arity = "2..*")
+            List<String> params;
+        }
+        ArrayParamsArity2_n params = CommandLine.populateCommand(new ArrayParamsArity2_n(), "a", "b", "c");
+        assertEquals(Arrays.asList("a", "b", "c"), params.params);
+
+        try {
+            params = CommandLine.populateCommand(new ArrayParamsArity2_n(), "a");
+            fail("Should not accept input with missing parameter");
+        } catch (MissingParameterException ex) {
+            assertEquals("positional parameter at index 0..* (<params>) requires at least 2 values, but only 1 were specified: [a]", ex.getMessage());
+        }
+
+        try {
+            params = CommandLine.populateCommand(new ArrayParamsArity2_n());
+            fail("Should not accept input with missing parameter");
+        } catch (MissingParameterException ex) {
+            assertEquals("positional parameter at index 0..* (<params>) requires at least 2 values, but none were specified.", ex.getMessage());
+        }
+    }
+
+    @Test
+    public void testNonVarargArrayParametersWithNegativeArityConsumesZeroArguments() {
+        class NonVarArgArrayParamsNegativeArity {
+            @Parameters(arity = "-1")
+            List<String> params;
+        }
+        try {
+            CommandLine.populateCommand(new NonVarArgArrayParamsNegativeArity(), "a", "b", "c");
+            fail("Expected UnmatchedArgumentException");
+        } catch (UnmatchedArgumentException ex) {
+            assertEquals("Unmatched arguments [a, b, c]", ex.getMessage());
+        }
+        try {
+            CommandLine.populateCommand(new NonVarArgArrayParamsNegativeArity(), "a");
+            fail("Expected UnmatchedArgumentException");
+        } catch (UnmatchedArgumentException ex) {
+            assertEquals("Unmatched argument [a]", ex.getMessage());
+        }
+        NonVarArgArrayParamsNegativeArity params = CommandLine.populateCommand(new NonVarArgArrayParamsNegativeArity());
+        assertEquals(null, params.params);
+    }
+
+    @Test
+    public void testNonVarargArrayParametersWithArity0() {
+        class NonVarArgArrayParamsZeroArity {
+            @Parameters(arity = "0")
+            List<String> params;
+        }
+        try {
+            CommandLine.populateCommand(new NonVarArgArrayParamsZeroArity(), "a", "b", "c");
+            fail("Expected UnmatchedArgumentException");
+        } catch (UnmatchedArgumentException ex) {
+            assertEquals("Unmatched arguments [a, b, c]", ex.getMessage());
+        }
+        try {
+            CommandLine.populateCommand(new NonVarArgArrayParamsZeroArity(), "a");
+            fail("Expected UnmatchedArgumentException");
+        } catch (UnmatchedArgumentException ex) {
+            assertEquals("Unmatched argument [a]", ex.getMessage());
+        }
+        NonVarArgArrayParamsZeroArity params = CommandLine.populateCommand(new NonVarArgArrayParamsZeroArity());
+        assertEquals(null, params.params);
+    }
+
+    @Test
+    public void testNonVarargArrayParametersWithArity1() {
+        class NonVarArgArrayParamsArity1 {
+            @Parameters(arity = "1")
+            List<String> params;
+        }
+        NonVarArgArrayParamsArity1 actual = CommandLine.populateCommand(new NonVarArgArrayParamsArity1(), "a", "b", "c");
+        assertEquals(Arrays.asList("a", "b", "c"), actual.params);
+
+        NonVarArgArrayParamsArity1  params = CommandLine.populateCommand(new NonVarArgArrayParamsArity1(), "a");
+        assertEquals(Arrays.asList("a"), params.params);
+
+        try {
+            params = CommandLine.populateCommand(new NonVarArgArrayParamsArity1());
+            fail("Should not accept input with missing parameter");
+        } catch (MissingParameterException ex) {
+            assertEquals("Missing required parameter: <params>", ex.getMessage());
+        }
+    }
+
+    @Test
+    public void testNonVarargArrayParametersWithArity2() {
+        class NonVarArgArrayParamsArity2 {
+            @Parameters(arity = "2")
+            List<String> params;
+        }
+        NonVarArgArrayParamsArity2 params = null;
+        try {
+            CommandLine.populateCommand(new NonVarArgArrayParamsArity2(), "a", "b", "c");
+            fail("expected MissingParameterException");
+        } catch (MissingParameterException ex) {
+            assertEquals("positional parameter at index 0..* (<params>) requires at least 2 values, but only 1 were specified: [c]", ex.getMessage());
+        }
+
+        try {
+            params = CommandLine.populateCommand(new NonVarArgArrayParamsArity2(), "a");
+            fail("Should not accept input with missing parameter");
+        } catch (MissingParameterException ex) {
+            assertEquals("positional parameter at index 0..* (<params>) requires at least 2 values, but only 1 were specified: [a]", ex.getMessage());
+        }
+
+        try {
+            params = CommandLine.populateCommand(new NonVarArgArrayParamsArity2());
+            fail("Should not accept input with missing parameter");
+        } catch (MissingParameterException ex) {
+            assertEquals("positional parameter at index 0..* (<params>) requires at least 2 values, but none were specified.", ex.getMessage());
+        }
+    }
+    @Test
+    public void testMixPositionalParamsWithOptions_ParamsUnboundedArity_isGreedy() {
+        class Arg {
+            @Parameters(arity = "1..*") List<String> parameters;
+            @Option(names = "-o")    List<String> options;
+        }
+        Arg result = CommandLine.populateCommand(new Arg(), "-o", "v1", "p1", "p2", "-o", "v2", "p3", "p4");
+        assertEquals(Arrays.asList("p1", "p2", "-o", "v2", "p3", "p4"), result.parameters);
+        assertEquals(Arrays.asList("v1"), result.options);
+
+        Arg result2 = CommandLine.populateCommand(new Arg(), "-o", "v1", "p1", "-o", "v2", "p3");
+        assertEquals(Arrays.asList("p1", "-o", "v2", "p3"), result2.parameters);
+        assertEquals(Arrays.asList("v1"), result2.options);
+
+        try {
+            CommandLine.populateCommand(new Arg(), "-o", "v1", "-o", "v2");
+            fail("Expected MissingParameterException");
+        } catch (MissingParameterException ex) {
+            assertEquals("Missing required parameters at positions 0..*: <parameters>", ex.getMessage());
+        }
+    }
+
+    @Test
+    public void test130MixPositionalParamsWithOptions() {
+        @CommandLine.Command(name = "test-command", description = "tests help from a command script")
+        class Arg {
+
+            @Parameters(description = "some parameters")
+            List<String> parameters;
+
+            @Option(names = {"-cp", "--codepath"}, description = "the codepath")
+            List<String> codepath;
+        }
+        Arg result = CommandLine.populateCommand(new Arg(), "--codepath", "/usr/x.jar", "placeholder", "-cp", "/bin/y.jar", "another");
+        assertEquals(Arrays.asList("/usr/x.jar", "/bin/y.jar"), result.codepath);
+        assertEquals(Arrays.asList("placeholder", "another"), result.parameters);
+    }
+
+    @Test
+    public void test130MixPositionalParamsWithOptions1() {
+        class Arg {
+            @Parameters           List<String> parameters;
+            @Option(names = "-o") List<String> options;
+        }
+        Arg result = CommandLine.populateCommand(new Arg(), "-o", "v1", "p1", "p2", "-o", "v2", "p3");
+        assertEquals(Arrays.asList("v1", "v2"), result.options);
+        assertEquals(Arrays.asList("p1", "p2", "p3"), result.parameters);
+    }
+
+    @Test
+    public void test130MixPositionalParamsWithOptionsArity() {
+        class Arg {
+            @Parameters(arity = "2") List<String> parameters;
+            @Option(names = "-o")    List<String> options;
+        }
+        Arg result = CommandLine.populateCommand(new Arg(), "-o", "v1", "p1", "p2", "-o", "v2", "p3", "p4");
+        assertEquals(Arrays.asList("v1", "v2"), result.options);
+        assertEquals(Arrays.asList("p1", "p2", "p3", "p4"), result.parameters);
+
+        Arg result2 = CommandLine.populateCommand(new Arg(), "-o", "v1", "p1", "-o", "v2", "p3");
+        assertEquals(Arrays.asList("v1"), result2.options);
+        assertEquals(Arrays.asList("p1", "-o", "v2", "p3"), result2.parameters);
+
+        try {
+            CommandLine.populateCommand(new Arg(), "-o", "v1", "p1", "p2", "-o", "v2", "p3");
+            fail("Expected MissingParameterException");
+        } catch (MissingParameterException ex) {
+            assertEquals("positional parameter at index 0..* (<parameters>) requires at least 2 values, but only 1 were specified: [p3]", ex.getMessage());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/23bcd257/log4j-core/src/test/java/org/apache/logging/log4j/core/tools/picocli/CommandLineHelpTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/tools/picocli/CommandLineHelpTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/tools/picocli/CommandLineHelpTest.java
index d0bb609..3651b22 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/tools/picocli/CommandLineHelpTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/tools/picocli/CommandLineHelpTest.java
@@ -2190,7 +2190,98 @@ public class CommandLineHelpTest {
         assertEquals(ansi.new Text("@|bold abc|@@|underline DE|@"), txt2.substring(0,5));
         assertEquals(ansi.new Text("@|bold bc|@@|underline DE|@"), txt2.substring(1,5));
     }
-
+    @Test
+    public void testTextSplitLines() {
+        Help.Ansi ansi = Help.Ansi.ON;
+        Text[] all = {
+                ansi.new Text("@|bold 012\n34|@").append("5\nAA\n6").append("@|underline 78\n90|@"),
+                ansi.new Text("@|bold 012\r34|@").append("5\rAA\r6").append("@|underline 78\r90|@"),
+                ansi.new Text("@|bold 012\r\n34|@").append("5\r\nAA\r\n6").append("@|underline 78\r\n90|@"),
+        };
+        for (Text text : all) {
+            Text[] lines = text.splitLines();
+            int i = 0;
+            assertEquals(ansi.new Text("@|bold 012|@"), lines[i++]);
+            assertEquals(ansi.new Text("@|bold 34|@5"), lines[i++]);
+            assertEquals(ansi.new Text("AA"), lines[i++]);
+            assertEquals(ansi.new Text("6@|underline 78|@"), lines[i++]);
+            assertEquals(ansi.new Text("@|underline 90|@"), lines[i++]);
+        }
+    }
+    @Test
+    public void testTextSplitLinesStartEnd() {
+        Help.Ansi ansi = Help.Ansi.ON;
+        Text[] all = {
+                ansi.new Text("\n@|bold 012\n34|@").append("5\nAA\n6").append("@|underline 78\n90|@\n"),
+                ansi.new Text("\r@|bold 012\r34|@").append("5\rAA\r6").append("@|underline 78\r90|@\r"),
+                ansi.new Text("\r\n@|bold 012\r\n34|@").append("5\r\nAA\r\n6").append("@|underline 78\r\n90|@\r\n"),
+        };
+        for (Text text : all) {
+            Text[] lines = text.splitLines();
+            int i = 0;
+            assertEquals(ansi.new Text(""), lines[i++]);
+            assertEquals(ansi.new Text("@|bold 012|@"), lines[i++]);
+            assertEquals(ansi.new Text("@|bold 34|@5"), lines[i++]);
+            assertEquals(ansi.new Text("AA"), lines[i++]);
+            assertEquals(ansi.new Text("6@|underline 78|@"), lines[i++]);
+            assertEquals(ansi.new Text("@|underline 90|@"), lines[i++]);
+            assertEquals(ansi.new Text(""), lines[i++]);
+        }
+    }
+    @Test
+    public void testTextSplitLinesStartEndIntermediate() {
+        Help.Ansi ansi = Help.Ansi.ON;
+        Text[] all = {
+                ansi.new Text("\n@|bold 012\n\n\n34|@").append("5\n\n\nAA\n\n\n6").append("@|underline 78\n90|@\n"),
+                ansi.new Text("\r@|bold 012\r\r\r34|@").append("5\r\r\rAA\r\r\r6").append("@|underline 78\r90|@\r"),
+                ansi.new Text("\r\n@|bold 012\r\n\r\n\r\n34|@").append("5\r\n\r\n\r\nAA\r\n\r\n\r\n6").append("@|underline 78\r\n90|@\r\n"),
+        };
+        for (Text text : all) {
+            Text[] lines = text.splitLines();
+            int i = 0;
+            assertEquals(ansi.new Text(""), lines[i++]);
+            assertEquals(ansi.new Text("@|bold 012|@"), lines[i++]);
+            assertEquals(ansi.new Text(""), lines[i++]);
+            assertEquals(ansi.new Text(""), lines[i++]);
+            assertEquals(ansi.new Text("@|bold 34|@5"), lines[i++]);
+            assertEquals(ansi.new Text(""), lines[i++]);
+            assertEquals(ansi.new Text(""), lines[i++]);
+            assertEquals(ansi.new Text("AA"), lines[i++]);
+            assertEquals(ansi.new Text(""), lines[i++]);
+            assertEquals(ansi.new Text(""), lines[i++]);
+            assertEquals(ansi.new Text("6@|underline 78|@"), lines[i++]);
+            assertEquals(ansi.new Text("@|underline 90|@"), lines[i++]);
+            assertEquals(ansi.new Text(""), lines[i++]);
+        }
+    }
+    @Test
+    public void testEmbeddedNewLinesInUsageSections() throws UnsupportedEncodingException {
+        @Command(description = "first line\nsecond line\nthird line", headerHeading = "headerHeading1\nheaderHeading2",
+                header = "header1\nheader2", descriptionHeading = "descriptionHeading1\ndescriptionHeading2",
+                footerHeading = "footerHeading1\nfooterHeading2", footer = "footer1\nfooter2")
+        class App {
+            @Option(names = {"-v", "--verbose"}, description = "optionDescription1\noptionDescription2") boolean v;
+            @Parameters(description = "paramDescription1\nparamDescription2") String file;
+        }
+        String actual = usageString(new App(), Help.Ansi.OFF);
+        String expected = String.format("" +
+                "headerHeading1%n" +
+                "headerHeading2header1%n" +
+                "header2%n" +
+                "Usage: <main class> [-v] <file>%n" +
+                "descriptionHeading1%n" +
+                "descriptionHeading2first line%n" +
+                "second line%n" +
+                "third line%n" +
+                "      <file>                  paramDescription1%n" +
+                "                              paramDescription2%n" +
+                "  -v, --verbose               optionDescription1%n" +
+                "                              optionDescription2%n" +
+                "footerHeading1%n" +
+                "footerHeading2footer1%n" +
+                "footer2%n");
+        assertEquals(expected, actual);
+    }
     @Test
     public void testTextWithMultipleStyledSections() {
         assertEquals("\u001B[1m<main class>\u001B[21m\u001B[0m [\u001B[33m-v\u001B[39m\u001B[0m] [\u001B[33m-c\u001B[39m\u001B[0m [\u001B[3m<count>\u001B[23m\u001B[0m]]",