candr.yoclip.DefaultParserHelpFactory.java Source code

Java tutorial

Introduction

Here is the source code for candr.yoclip.DefaultParserHelpFactory.java

Source

/*
 * 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 candr.yoclip;

import candr.yoclip.annotation.Options;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.StrBuilder;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

/**
 * The default implementation of the {@link ParserHelpFactory} API.
 */
public class DefaultParserHelpFactory<T> implements ParserHelpFactory<T> {

    protected static final String DEFAULT_HEADER = "Options:";

    @Override
    public String createUsage(ParserOptions<T> optionParameters) {

        if (null == optionParameters) {
            throw new IllegalArgumentException("OptionParameters cannot be null.");
        }

        int optionCount = 0;
        boolean hasRequiredOption = false;
        boolean hasArguments = false;
        boolean hasRequiredArguments = false;
        for (final ParserOption<T> optionParameter : optionParameters.get()) {

            if (optionParameter.isArguments()) {

                hasArguments = true;
                if (optionParameter.isRequired()) {
                    hasRequiredArguments = true;
                }

            } else {

                optionCount++;
                if (optionParameter.isRequired()) {
                    hasRequiredOption = true;
                }
            }
        }

        final StrBuilder builder = new StrBuilder();

        if (optionCount > 0) {
            if (hasRequiredOption) {
                builder.append("OPTION");
                if (optionCount > 1) {
                    builder.append(" [OPTION ...]");
                }
            } else if (optionCount == 1) {
                builder.append("[OPTION]");
            } else {
                builder.append("[OPTION [OPTION ...]]");
            }
        }

        if (hasArguments) {
            if (builder.length() > 0) {
                builder.append(' ');
            }
            if (hasRequiredArguments) {
                builder.append("ARGS");
            } else {
                builder.append("[ARGS]");
            }
        }
        return builder.toString();
    }

    @Override
    public String getHeaderDescription(ParserOptions<T> optionParameters) {

        if (null == optionParameters) {
            throw new IllegalArgumentException("OptionParameters cannot be null.");
        }

        String header = optionParameters.getHeader();
        if (StringUtils.isEmpty(header)) {
            header = DEFAULT_HEADER;
        }
        return header;
    }

    @Override
    public String getTrailerDescription(ParserOptions<T> optionParameters) {

        if (null == optionParameters) {
            throw new IllegalArgumentException("OptionParameters cannot be null.");
        }

        return optionParameters.getTrailer();
    }

    @Override
    public List<Pair<String, String>> getOptionPropertyDescriptions(ParserOption<T> parserOption) {

        if (null == parserOption) {
            throw new IllegalArgumentException("ParserOption cannot be null.");
        }

        List<Pair<String, String>> propertyDescriptions = parserOption.getPropertyDescriptions();
        if (propertyDescriptions.isEmpty()) {
            return Collections.emptyList();
        }

        List<Pair<String, String>> optionPropertiesHelp = new LinkedList<Pair<String, String>>();
        for (final Pair<String, String> propertyDescription : propertyDescriptions) {

            String synopsis = propertyDescription.getKey();
            if (StringUtils.isEmpty(synopsis)) {
                synopsis = "key";
            }

            String details = propertyDescription.getValue();
            if (StringUtils.isEmpty(details)) {
                details = String.format("An option property value for %s.", parserOption);
            }

            optionPropertiesHelp.add(ImmutablePair.of(synopsis, details));
        }

        return optionPropertiesHelp;
    }

    @Override
    public Pair<String, String> getOptionDescription(String prefix, String separator,
            ParserOption<T> parserOption) {

        if (null == parserOption) {
            throw new IllegalArgumentException("ParserOption cannot be null.");
        }

        String usage = parserOption.getUsage();
        if (StringUtils.isEmpty(usage)) {
            usage = createParserParameterUsage(prefix, separator, parserOption);
        }

        String description = parserOption.getDescription();
        if (StringUtils.isEmpty(description)) {
            description = createParserParameterDescription(parserOption);
        }

        return ImmutablePair.of(usage, description);
    }

    @Override
    public String wrap(String text, int width) {
        return hangingIndentWrap(text, 0, width);
    }

    @Override
    public String hangingIndentWrap(String text, int hangingIndent, int width) {

        if (StringUtils.isEmpty(text)) {
            return StringUtils.EMPTY;
        }

        // make sure the hanging indent is reasonable
        hangingIndent = Math.max(hangingIndent, 0);
        int availableTextWidth = width - hangingIndent;
        if (availableTextWidth < 1) {
            final String error = String.format("Indent (%d) too large for width (%d).", hangingIndent, width);
            throw new OptionsParseException(error);
        }

        final StrBuilder builder = new StrBuilder();

        // break the string into pieces based on the new line marker
        boolean builderWhitespaceOnly = true;
        final String[] lines = StringUtils.splitByWholeSeparatorPreserveAllTokens(text, Options.LINE_BREAK);
        for (int lineIndex = 0; lineIndex < lines.length; lineIndex++) {

            // account for the new line markers
            if (lineIndex > 0) {
                builder.appendNewLine();
            }

            // for each line make sure it is wrapped to the line width
            String line = lines[lineIndex];
            for (int wrapCount = 0; !StringUtils.isEmpty(line); wrapCount++) {

                // account for the text being wrapped
                if (wrapCount > 0) {
                    builder.appendNewLine();
                }

                // once text is added to the builder the line width changes based on the hanging indent
                int lineWidth;
                if (builderWhitespaceOnly) {
                    lineWidth = width;
                    builderWhitespaceOnly = false;

                } else {
                    lineWidth = availableTextWidth;
                    if (hangingIndent > 0) {
                        builder.appendPadding(hangingIndent, ' ');
                    }
                }

                if (line.length() <= lineWidth) {
                    builder.append(line);
                    break;
                }

                // look for a natural break in the text, otherwise force one
                int wrapPosition = line.lastIndexOf(' ', lineWidth);
                if (wrapPosition < 0) {
                    wrapPosition = lineWidth;
                }
                builder.append(line.substring(0, wrapPosition));
                line = line.substring(wrapPosition).trim();
            }
        }

        return builder.toString();
    }

    /**
     * Creates a usage description from the parser parameter.
     *
     * @param prefix       The parser parameter prefix (see {@link ParserOptions#getPrefix() getPrefix()} for more
     *                     information).
     * @param separator    The parser parameter name and value separator (see {@link ParserOptions#getSeparator()
     *                     getSeparator()} for more information}.
     * @param parserOption The parser parameter a usage will be generated for.
     * @return a usage string for the parser parameter.
     * @throws IllegalArgumentException if the {@code prefix} or {@code separator} parameter values are empty.
     * @throws NullPointerException     if the parser parameter is null.
     */
    protected String createParserParameterUsage(final String prefix, final String separator,
            final ParserOption<T> parserOption) {
        if (StringUtils.isEmpty(prefix)) {
            throw new IllegalArgumentException("The ParserOption prefix cannot be empty.");
        }
        if (StringUtils.isEmpty(separator)) {
            throw new IllegalArgumentException("The ParserOption separator cannot be empty.");
        }

        final StrBuilder usage = new StrBuilder();

        if (parserOption.isProperties()) {
            final String name = parserOption.getNames()[0];
            usage.append(prefix).append(name).append("KEY=VALUE");

        } else if (parserOption.isArguments()) {
            usage.append("ARG");

        } else {
            final String[] names = parserOption.getNames();
            for (int i = 0; i < names.length; i++) {
                if (i > 0) {
                    usage.append("|");
                }
                usage.append(prefix).append(names[i]);
            }
            if (parserOption.hasValue()) {
                usage.append(separator);
                final String typeDescription = ClassUtils.getShortClassName(parserOption.getType());
                usage.append(typeDescription.toUpperCase());
            }
        }

        return usage.toString();
    }

    /**
     * Creates a description for the parser parameter.
     *
     * @param parserOption The parser parameter a description will be created for.
     * @return a description of the parser parameter.
     */
    protected String createParserParameterDescription(final ParserOption<T> parserOption) {

        if (null == parserOption) {
            throw new IllegalArgumentException("ParserOption cannot be null.");
        }

        String description;
        if (parserOption.isProperties()) {
            description = String.format("Sets a property and value into the '%s' Map field.",
                    parserOption.getUniqueId());

        } else if (parserOption.isArguments()) {
            description = String.format("Sets an argument into the '%s' List field.", parserOption.getUniqueId());

        } else {
            description = String.format("Sets the '%s' field option.", parserOption.getUniqueId());
        }
        if (parserOption.isRequired()) {
            description = description.concat(" This option is required.");
        }
        return description;
    }
}