static boolean excluded(String fqcn, Tracer tracer) { String[] excludes = System.getProperty("picocli.converters.excludes", "").split(","); for (String regex : excludes) { if (fqcn.matches(regex)) { tracer.debug("BuiltIn type converter for %s is not loaded: (picocli.converters.excludes=%s)%n", fqcn, System.getProperty("picocli.converters.excludes")); return true; } } return false; } static Set<String> traced = new HashSet<String>();
public static void registerIfAvailable(Map<Class<?>, ITypeConverter<?>> registry, Tracer tracer) { if (excluded(FQCN, tracer)) { return; } try { registry.put(Class.forName(FQCN), new ISO8601TimeConverter()); } catch (Exception e) { if (!traced.contains(FQCN)) { tracer.debug("Could not register converter for %s: %s%n", FQCN, e.toString()); } traced.add(FQCN); } } }
static void registerIfAvailable(Map<Class<?>, ITypeConverter<?>> registry, Tracer tracer, String fqcn, String factoryClass, String factoryMethodName, Class<?>... paramTypes) { if (excluded(fqcn, tracer)) { return; } try { Class<?> cls = Class.forName(fqcn); Class<?> factory = Class.forName(factoryClass); Method method = factory.getDeclaredMethod(factoryMethodName, paramTypes); registry.put(cls, new ReflectionConverter(method, paramTypes)); } catch (Exception e) { if (!traced.contains(fqcn)) { tracer.debug("Could not register converter for %s: %s%n", fqcn, e.toString()); } traced.add(fqcn); } } static boolean excluded(String fqcn, Tracer tracer) {
static boolean excluded(String fqcn, Tracer tracer) { String[] excludes = System.getProperty("picocli.converters.excludes", "").split(","); for (String regex : excludes) { if (fqcn.matches(regex)) { tracer.debug("BuiltIn type converter for %s is not loaded: (picocli.converters.excludes=%s)%n", fqcn, System.getProperty("picocli.converters.excludes")); return true; } } return false; } static Set<String> traced = new HashSet<String>();
if (tracer.isDebug()) {tracer.debug("Processing argument '%s'. Remainder=%s%n", arg, reverse(copy(args)));} throw MissingParameterException.create(CommandLine.this, required, separator); if (tracer.isDebug()) {tracer.debug("Found subcommand '%s' (%s)%n", arg, subcommand.commandSpec.toString());} subcommand.interpreter.parse(parsedCommands, args, originalArgs, nowProcessing); parseResult.subcommand(subcommand.interpreter.parseResult.build()); args.push(optionParam); arg = key; if (tracer.isDebug()) {tracer.debug("Separated '%s' option from '%s' option parameter%n", key, optionParam);} } else { if (tracer.isDebug()) {tracer.debug("'%s' contains separator '%s' but '%s' is not a known option%n", arg, separator, key);} if (tracer.isDebug()) {tracer.debug("'%s' cannot be separated into <option>%s<option-parameter>%n", arg, separator);} if (tracer.isDebug()) {tracer.debug("Trying to process '%s' as clustered short options%n", arg, args);} processClusteredShortOptions(required, initialized, arg, args); if (tracer.isDebug()) {tracer.debug("Could not find option '%s', deciding whether to treat as unmatched option or positional parameter...%n", arg);} if (commandSpec.resemblesOption(arg, tracer)) { handleUnmatchedArgument(args); continue; } // #149 if (tracer.isDebug()) {tracer.debug("No option named '%s' found. Processing remainder as positional parameters%n", arg);} processPositionalParameter(required, initialized, args);
Range arity = argSpec.arity(); String argDescription = "option " + prefix + cluster.charAt(0); if (tracer.isDebug()) {tracer.debug("Found option '%s%s' in %s: %s, arity=%s%n", prefix, cluster.charAt(0), arg, argSpec, arity);} required.remove(argSpec); if (tracer.isDebug()) {tracer.debug("Trying to process '%s' as option parameter%n", cluster);} args.push(paramAttachedToOption ? prefix + cluster : cluster); if (args.peek().equals(arg)) { // #149 be consistent between unmatched short and long options if (tracer.isDebug()) {tracer.debug("Could not match any short options in %s, deciding whether to treat as unmatched option or positional parameter...%n", arg);} if (commandSpec.resemblesOption(arg, tracer)) { handleUnmatchedArgument(args); return; } // #149 processPositionalParameter(required, initialized, args); if (tracer.isDebug()) {tracer.debug("No option found for %s in %s%n", cluster, arg);} handleUnmatchedArgument(args); } else { args.push(cluster); if (tracer.isDebug()) {tracer.debug("%s is not an option parameter for %s%n", cluster, arg);} processPositionalParameter(required, initialized, args);
static CommandSpec extractCommandSpec(Object command, IFactory factory, boolean annotationsAreMandatory) { Class<?> cls = command.getClass(); Tracer t = new Tracer(); t.debug("Creating CommandSpec for object of class %s with factory %s%n", cls.getName(), factory.getClass().getName()); if (command instanceof CommandSpec) { return (CommandSpec) command; } Object instance = command; commandClassName = cls.getName(); try { t.debug("Getting a %s instance from the factory%n", cls.getName()); instance = DefaultFactory.create(factory, cls); cls = instance.getClass(); commandClassName = cls.getName(); t.debug("Factory returned a %s instance%n", commandClassName); } catch (InitializationException ex) { if (cls.isInterface()) { t.debug("%s. Creating Proxy for interface %s%n", ex.getCause(), cls.getName()); instance = Proxy.newProxyInstance(cls.getClassLoader(), new Class<?>[]{cls}, new PicocliInvocationHandler()); } else { t.debug("Using method %s as command %n", method); commandClassName = method.toString(); Command cmd = method.getAnnotation(Command.class);
private void parse(List<CommandLine> parsedCommands, Stack<String> argumentStack, String[] originalArgs, List<Object> nowProcessing) { if (tracer.isDebug()) {tracer.debug("Initializing %s: %d options, %d positional parameters, %d required, %d subcommands.%n", commandSpec.toString(), new HashSet<ArgSpec>(commandSpec.optionsMap().values()).size(), commandSpec.positionalParameters().size(), commandSpec.requiredArgs().size(), commandSpec
private void processPositionalParameter(Collection<ArgSpec> required, Set<ArgSpec> initialized, Stack<String> args) throws Exception { if (tracer.isDebug()) {tracer.debug("Processing next arg as a positional parameter at index=%d. Remainder=%s%n", position, reverse(copy(args)));} if (config().stopAtPositional()) { if (!endOfOptions && tracer.isDebug()) {tracer.debug("Parser was configured with stopAtPositional=true, treating remaining arguments as positional parameters.%n");} endOfOptions = true; 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);} if (!assertNoMissingParameters(positionalParam, arity, argsCopy)) { break; } // #389 collectErrors parsing int originalSize = argsCopy.size(); if (tracer.isDebug()) {tracer.debug("Consumed %d arguments and %d interactive values, moving position to index %d.%n", argsConsumed, interactiveConsumed, position);} if (argsConsumed == 0 && interactiveConsumed == 0 && !args.isEmpty()) { handleUnmatchedArgument(args);
String name = argSpec.isOption() ? ((OptionSpec) argSpec).longestName() : "position " + position; String prompt = String.format("Enter value for %s (%s): ", name, str(argSpec.renderedDescription(), 0)); if (tracer.isDebug()) {tracer.debug("Reading value for %s from console...%n", name);} char[] value = readPassword(prompt); if (tracer.isDebug()) {tracer.debug("User entered '%s' for %s.%n", value, name);} workingStack.push(new String(value));
@SuppressWarnings("unchecked") private int applyValuesToCollectionField(ArgSpec argSpec, LookBehind lookBehind, Range arity, Stack<String> args, Set<ArgSpec> initialized, String argDescription) throws Exception { Collection<Object> collection = (Collection<Object>) argSpec.getValue(); Class<?> type = argSpec.auxiliaryTypes()[0]; List<Object> converted = consumeArguments(argSpec, lookBehind, arity, args, type, argDescription); if (collection == null || (!collection.isEmpty() && !initialized.contains(argSpec))) { tracer.debug("Initializing binding for %s with empty %s%n", optionDescription("", argSpec, 0), argSpec.type().getSimpleName()); collection = createCollection(argSpec.type()); // collection type argSpec.setValue(collection); } initialized.add(argSpec); for (Object element : converted) { if (element instanceof Collection<?>) { collection.addAll((Collection<?>) element); } else { collection.add(element); } } parseResult.add(argSpec, position); argSpec.setValue(collection); return converted.size(); }
boolean resemblesOption(String arg, Tracer tracer) { if (parser().unmatchedOptionsArePositionalParams()) { if (tracer != null && tracer.isDebug()) {tracer.debug("Parser is configured to treat all unmatched options as positional parameter%n", arg);} return false; } if (arg.length() == 1) { if (tracer != null && tracer.isDebug()) {tracer.debug("Single-character arguments that don't match known options are considered positional parameters%n", arg);} return false; } if (options().isEmpty()) { boolean result = arg.startsWith("-"); if (tracer != null && tracer.isDebug()) {tracer.debug("'%s' %s an option%n", arg, (result ? "resembles" : "doesn't resemble"));} return result; } int count = 0; for (String optionName : optionsMap().keySet()) { for (int i = 0; i < arg.length(); i++) { if (optionName.length() > i && arg.charAt(i) == optionName.charAt(i)) { count++; } else { break; } } } boolean result = count > 0 && count * 10 >= optionsMap().size() * 9; // at least one prefix char in common with 9 out of 10 options if (tracer != null && tracer.isDebug()) {tracer.debug("'%s' %s an option: %d matching prefix chars out of %d option names%n", arg, (result ? "resembles" : "doesn't resemble"), count, optionsMap().size());} return result; } }
private int applyValuesToMapField(ArgSpec argSpec, LookBehind lookBehind, Range arity, Stack<String> args, Set<ArgSpec> initialized, String argDescription) throws Exception { Class<?>[] classes = argSpec.auxiliaryTypes(); if (classes.length < 2) { throw new ParameterException(CommandLine.this, argSpec.toString() + " needs two types (one for the map key, one for the value) but only has " + classes.length + " types configured.",argSpec, null); } ITypeConverter<?> keyConverter = getTypeConverter(classes[0], argSpec, 0); ITypeConverter<?> valueConverter = getTypeConverter(classes[1], argSpec, 1); @SuppressWarnings("unchecked") Map<Object, Object> map = (Map<Object, Object>) argSpec.getValue(); if (map == null || (!map.isEmpty() && !initialized.contains(argSpec))) { tracer.debug("Initializing binding for %s with empty %s%n", optionDescription("", argSpec, 0), argSpec.type().getSimpleName()); map = createMap(argSpec.type()); // map class argSpec.setValue(map); } initialized.add(argSpec); int originalSize = map.size(); consumeMapArguments(argSpec, lookBehind, arity, args, classes, keyConverter, valueConverter, map, argDescription); parseResult.add(argSpec, position); argSpec.setValue(map); return map.size() - originalSize; }
/** * Entry point into parsing command line arguments. * @param args the command line arguments * @return a list with all commands and subcommands initialized by this method * @throws ParameterException if the specified command line arguments are invalid */ List<CommandLine> parse(String... args) { Assert.notNull(args, "argument array"); if (tracer.isInfo()) {tracer.info("Picocli version: %s%n", versionString());} if (tracer.isInfo()) {tracer.info("Parsing %d command line args %s%n", args.length, Arrays.toString(args));} if (tracer.isDebug()){tracer.debug("Parser configuration: %s%n", config());} if (tracer.isDebug()){tracer.debug("(ANSI is %s by default: isatty=%s, XTERM=%s, OSTYPE=%s, isWindows=%s, JansiConsoleInstalled=%s, ANSICON=%s, ConEmuANSI=%s, NO_COLOR=%s, CLICOLOR=%s, CLICOLOR_FORCE=%s)%n", Help.Ansi.ansiPossible() ? "enabled" : "disabled", Help.Ansi.isTTY(), System.getenv("XTERM"), System.getenv("OSTYPE"), Help.Ansi.isWindows(), Help.Ansi.isJansiConsoleInstalled(), System.getenv("ANSICON"), System.getenv("ConEmuANSI"), System.getenv("NO_COLOR"), System.getenv("CLICOLOR"), System.getenv("CLICOLOR_FORCE"));} List<String> expanded = new ArrayList<String>(); for (String arg : args) { addOrExpand(arg, expanded, new LinkedHashSet<String>()); } Stack<String> arguments = new Stack<String>(); arguments.addAll(reverseList(expanded)); List<CommandLine> result = new ArrayList<CommandLine>(); parse(result, arguments, args, new ArrayList<Object>()); return result; }
private boolean canConsumeOneArgument(ArgSpec argSpec, Range arity, int consumed, String arg, Class<?> type, String argDescription) { ITypeConverter<?> converter = getTypeConverter(type, argSpec, 0); try { String[] values = argSpec.splitValue(trim(arg), commandSpec.parser(), arity, consumed); // if (!argSpec.acceptsValues(values.length, commandSpec.parser())) { // tracer.debug("$s would split into %s values but %s cannot accept that many values.%n", arg, values.length, argDescription); // return false; // } for (String value : values) { tryConvert(argSpec, -1, converter, value, type); } return true; } catch (PicocliException ex) { tracer.debug("$s cannot be assigned to %s: type conversion fails: %s.%n", arg, argDescription, ex.getMessage()); return false; } }
private boolean canConsumeOneMapArgument(ArgSpec argSpec, Range arity, int consumed, String raw, Class<?>[] classes, ITypeConverter<?> keyConverter, ITypeConverter<?> valueConverter, String argDescription) { String[] values = argSpec.splitValue(raw, commandSpec.parser(), arity, consumed); try { for (String value : values) { String[] keyValue = splitKeyValue(argSpec, value); tryConvert(argSpec, -1, keyConverter, keyValue[0], classes[0]); tryConvert(argSpec, -1, valueConverter, keyValue[1], classes[1]); } return true; } catch (PicocliException ex) { tracer.debug("$s cannot be assigned to %s: type conversion fails: %s.%n", raw, argDescription, ex.getMessage()); return false; } }
private void clear(ArgSpec argSpec) { argSpec.resetStringValues(); argSpec.resetOriginalStringValues(); argSpec.typedValues.clear(); argSpec.typedValueAtPosition.clear(); if (argSpec.hasInitialValue()) { try { argSpec.setter().set(argSpec.initialValue()); tracer.debug("Set initial value for %s of type %s to %s.%n", argSpec, argSpec.type(), String.valueOf(argSpec.initialValue())); } catch (Exception ex) { tracer.warn("Could not set initial value for %s of type %s to %s: %s%n", argSpec, argSpec.type(), String.valueOf(argSpec.initialValue()), ex); } } else { tracer.debug("Initial value not available for %s%n", argSpec); } } private void maybeThrow(PicocliException ex) throws PicocliException {
private void processStandaloneOption(Collection<ArgSpec> required, Set<ArgSpec> initialized, String arg, Stack<String> args, boolean paramAttachedToKey) throws Exception { ArgSpec argSpec = commandSpec.optionsMap().get(arg); required.remove(argSpec); Range arity = argSpec.arity(); if (paramAttachedToKey) { arity = arity.min(Math.max(1, arity.min)); // if key=value, minimum arity is at least 1 } LookBehind lookBehind = paramAttachedToKey ? LookBehind.ATTACHED_WITH_SEPARATOR : LookBehind.SEPARATE; if (tracer.isDebug()) {tracer.debug("Found option named '%s': %s, arity=%s%n", arg, argSpec, arity);} parseResult.nowProcessing.add(argSpec); applyOption(argSpec, lookBehind, arity, args, initialized, "option " + arg); }
private void applyDefault(IDefaultValueProvider defaultValueProvider, ArgSpec arg, List<ArgSpec> required) throws Exception { // Default value provider return value is only used if provider exists and if value // is not null otherwise the original default or initial value are used String fromProvider = defaultValueProvider == null ? null : defaultValueProvider.defaultValue(arg); String defaultValue = fromProvider == null ? arg.defaultValue() : fromProvider; if (defaultValue == null) { return; } if (tracer.isDebug()) {tracer.debug("Applying defaultValue (%s) to %s%n", defaultValue, arg);} Range arity = arg.arity().min(Math.max(1, arg.arity().min)); applyOption(arg, LookBehind.SEPARATE, arity, stack(defaultValue), new HashSet<ArgSpec>(), arg.toString); required.remove(arg); }
private String[] debug(String[] result, String msg, String value) { Tracer t = new Tracer(); if (t.isDebug()) {t.debug("%s with regex '%s' resulted in %s parts: %s%n", msg, splitRegex(), result.length, Arrays.asList(result));} return result; } // @since 3.7