com.github.rvesse.airline.parser.options.ListValueOptionParser.java Source code

Java tutorial

Introduction

Here is the source code for com.github.rvesse.airline.parser.options.ListValueOptionParser.java

Source

/**
 * Copyright (C) 2010-16 the original author or authors.
 *
 * Licensed 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 com.github.rvesse.airline.parser.options;

import java.util.List;
import org.apache.commons.collections4.iterators.PeekingIterator;
import org.apache.commons.lang3.StringUtils;

import com.github.rvesse.airline.Context;
import com.github.rvesse.airline.model.OptionMetadata;
import com.github.rvesse.airline.parser.ParseState;
import com.github.rvesse.airline.parser.errors.ParseOptionMissingValueException;
import com.github.rvesse.airline.parser.errors.ParseOptionUnexpectedException;
import com.github.rvesse.airline.utils.AirlineUtils;

/**
 * An options parser that expects the name and value(s) to be white space
 * separated e.g. {@code --name value} but which allows for the values to be a
 * non-whitespace separated list
 * <p>
 * So for example {@code --name foo,bar} would be treated as the values
 * {@code foo} and {@code bar} passed to the {@code --name} option. This parser
 * differs from the {@link StandardOptionParser} in that the standard parser
 * would treat {@code foo,bar} as a single value passed to the name option. This
 * parser expects that the list it receives contains the correct number of items
 * for the arity of the option and if not produces an error
 * </p>
 * <p>
 * You can also omit the whitespace between the name and the value list when
 * using a single character name of the option similar to how the
 * {@link ClassicGetOptParser} works. For example {@code -nfoo,bar} is
 * equivalent to our previous example assuming that {@code -n} is an alternative
 * name for the same option as {@code --name}.
 * </p>
 * <p>
 * The default separator for values is {@code ,} but this can be configured as
 * desired.
 * </p>
 *
 */
public class ListValueOptionParser<T> extends AbstractOptionParser<T> {

    private static final char DEFAULT_SEPARATOR = ',';
    private final char separator;

    public ListValueOptionParser() {
        this(DEFAULT_SEPARATOR);
    }

    public ListValueOptionParser(char separator) {
        if (Character.isWhitespace(separator))
            throw new IllegalArgumentException("List separator character cannot be a whitespace character");
        this.separator = separator;
    }

    protected final List<String> getValues(String list) {
        return AirlineUtils.arrayToList(StringUtils.split(list, this.separator));
    }

    @Override
    public ParseState<T> parseOptions(PeekingIterator<String> tokens, ParseState<T> state,
            List<OptionMetadata> allowedOptions) {
        String name = tokens.peek();
        boolean noSep = false;
        OptionMetadata option = findOption(state, allowedOptions, name);
        if (option == null) {
            // Check if we are looking at a maven style -Pa,b,c argument
            if (hasShortNamePrefix(name) && name.length() > 2) {
                String shortName = name.substring(0, 2);
                option = findOption(state, allowedOptions, shortName);
                noSep = option != null;
            }

            if (!noSep)
                return null;
        }

        tokens.next();
        state = state.pushContext(Context.OPTION).withOption(option);

        String list = noSep ? name.substring(2) : null;
        if (option.getArity() == 0) {
            // Zero arity option, consume token and continue
            state = state.withOptionValue(option, Boolean.TRUE.toString()).popContext();
        } else {
            if (list == null) {
                // Can't parse list value if there are no further tokens
                if (!tokens.hasNext())
                    return state;

                // Consume the value immediately, this option parser will now
                // either succeed to parse the option or will error
                list = tokens.next();
            }

            // Parse value as a list
            List<String> listValues = getValues(list);
            if (listValues.size() < option.getArity())
                throw new ParseOptionMissingValueException(
                        "Too few option values received for option %s in list value '%s' (%d values expected but only found %d)",
                        option.getTitle(), option.getOptions().iterator().next(), list, option.getArity(),
                        listValues.size());
            if (listValues.size() > option.getArity())
                throw new ParseOptionUnexpectedException(
                        "Too many option values received for option %s in list value '%s' (%d values expected but found %d)",
                        option.getOptions().iterator().next(), list, option.getArity(), listValues.size());

            // Parse individual values and assign to option
            for (String value : listValues) {
                state = state.withOptionValue(option, value);
            }

            state = state.popContext();

        }
        return state;
    }

}