candr.yoclip.option.OptionSetter.java Source code

Java tutorial

Introduction

Here is the source code for candr.yoclip.option.OptionSetter.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.option;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

import org.apache.commons.lang3.StringUtils;

import candr.yoclip.OptionUtils;
import candr.yoclip.OptionsBadNameException;
import candr.yoclip.OptionsParseException;
import candr.yoclip.ParserOption;
import candr.yoclip.annotation.Option;

/**
 * Associates an {@link Option} annotation with a Java bean setter {@code Method} in a class that contains option parser annotations.
 */
class OptionSetter<T> extends AbstractSetterOption<T> implements ParserOption<T> {

    /**
     * The {@link Option} annotation associated with a Java bean setter {@code Method}.
     */
    final private Option option;

    /**
     * Initializes the method descriptor to create an association between the bean setter {@code Method} and the {@code Option}
     * annotation.
     *
     * @param option The annotation associated with the bean setter method.
     * @param setter The bean setter method annotated with {@code Option}.
     * @throws OptionsBadNameException       if one of the option {@link Option#name() name} is empty or the option does not have at
     *                                       least one name.
     * @throws UnsupportedOperationException if the setter method requires more than one parameter.
     */
    public OptionSetter(final Option option, final Method setter) {

        super(setter);

        final Class<?>[] parameterTypes = setter.getParameterTypes();
        if (1 != parameterTypes.length) {
            throw new UnsupportedOperationException(setter.toGenericString() + " must have 1 parameter.");
        }

        for (final String name : option.name()) {
            if (StringUtils.isEmpty(name)) {
                throw new OptionsBadNameException(setter.getName() + " has an option name that is empty.");
            }
        }
        if (option.name().length == 0) {
            throw new OptionsBadNameException("Options must have at least one name.");
        }

        this.option = option;
    }

    /**
     * Get the names associated with the option.
     *
     * @return the option names as a string array or an empty array if the option does not have a name associated with it.
     */
    @Override
    public String[] getNames() {

        return Arrays.copyOf(option.name(), option.name().length);
    }

    /**
     * Gets the {@link Option#usage() Option usage} field. Please refer to the {@link candr.yoclip.OptionUtils#combine(String[]) combine}
     * method for rules on how the usage will be formatted.
     *
     * @return the option usage or empty string if the usage has not been set.
     */
    @Override
    public String getUsage() {

        return OptionUtils.combine(option.usage());
    }

    /**
     * Gets the {@link Option#description() Option description} field. Please refer to
     * the {@link candr.yoclip.OptionUtils#combine(String[]) combine} method for rules on how the description will be formatted.
     *
     * @return the option description or empty string if the description has not been set.
     */
    @Override
    public String getDescription() {

        return OptionUtils.combine(option.description());
    }

    /**
     * Gets the {@link Option#required() Option required} field.
     *
     * @return {@code true} if the option is required, {@code false} otherwise.
     */
    @Override
    public boolean isRequired() {

        return option.required();
    }

    /**
     * Indicates when an option is used a value must also be included.
     *
     * @return {@code true} is a value is required, {@code false} otherwise.
     */
    @Override
    public boolean hasValue() {

        return (!(Boolean.TYPE.equals(getType()) || Boolean.class.isAssignableFrom(getType())))
                || option.hasValue();
    }

    /**
     * Used to flag the option as requesting help.
     *
     * @return {@code true} if the option is help related, {@code false} otherwise.
     */
    @Override
    public boolean isHelp() {

        return option.help();
    }

    @Override
    public void setOption(final T bean, final String value) {

        final Class<?> setterParameterType = getType();
        try {

            if (Boolean.TYPE.equals(setterParameterType) || Boolean.class.isAssignableFrom(setterParameterType)) {
                setBooleanOption(bean, value);

            } else if (Byte.TYPE.equals(setterParameterType) || Byte.class.isAssignableFrom(setterParameterType)) {
                setByteOption(bean, value);

            } else if (Short.TYPE.equals(setterParameterType)
                    || Short.class.isAssignableFrom(setterParameterType)) {
                setShortOption(bean, value);

            } else if (Integer.TYPE.equals(setterParameterType)
                    || Integer.class.isAssignableFrom(setterParameterType)) {
                setIntegerOption(bean, value);

            } else if (Long.TYPE.equals(setterParameterType) || Long.class.isAssignableFrom(setterParameterType)) {
                setLongOption(bean, value);

            } else if (Character.TYPE.equals(setterParameterType)
                    || Character.class.isAssignableFrom(setterParameterType)) {
                setCharacterOption(bean, value);

            } else if (Float.TYPE.equals(setterParameterType)
                    || Float.class.isAssignableFrom(setterParameterType)) {
                setFloatOption(bean, value);

            } else if (Double.TYPE.equals(setterParameterType)
                    || Double.class.isAssignableFrom(setterParameterType)) {
                setDoubleOption(bean, value);

            } else if (String.class.isAssignableFrom(setterParameterType)) {
                setStringOption(bean, value);

            } else {
                final String supportedTypes = "boolean, byte, short, int, long, char, float, double, and String";
                throw new OptionsParseException(
                        String.format("OptionParameter only supports %s types", supportedTypes));
            }

        } catch (NumberFormatException e) {
            final String error = String.format("Error converting '%s' to %s", value, setterParameterType);
            throw new OptionsParseException(error, e);
        }
    }

    protected void setBooleanOption(final T bean, final String value) {

        final Boolean booleanValue = Boolean.valueOf(value);
        set(bean, new Value<T>() {

            public void set(final T bean, final Method setter)
                    throws InvocationTargetException, IllegalAccessException {

                setter.invoke(bean, booleanValue);
            }
        });
    }

    protected void setByteOption(final T bean, final String value) {

        final Byte byteValue = StringUtils.isEmpty(value) ? 0 : Byte.valueOf(value);
        set(bean, new Value<T>() {

            public void set(final T bean, final Method setter)
                    throws InvocationTargetException, IllegalAccessException {

                setter.invoke(bean, byteValue);
            }
        });
    }

    protected void setShortOption(final T bean, final String value) {

        final Short shortValue = StringUtils.isEmpty(value) ? 0 : Short.valueOf(value);
        set(bean, new Value<T>() {

            public void set(final T bean, final Method setter)
                    throws InvocationTargetException, IllegalAccessException {

                setter.invoke(bean, shortValue);
            }
        });
    }

    protected void setIntegerOption(final T bean, final String value) {

        final Integer integerValue = StringUtils.isEmpty(value) ? 0 : Integer.valueOf(value);
        set(bean, new Value<T>() {

            public void set(final T bean, final Method setter)
                    throws InvocationTargetException, IllegalAccessException {

                setter.invoke(bean, integerValue);
            }
        });
    }

    protected void setLongOption(final T bean, final String value) {

        final Long longValue = StringUtils.isEmpty(value) ? 0L : Long.valueOf(value);
        set(bean, new Value<T>() {

            public void set(final T bean, final Method setter)
                    throws InvocationTargetException, IllegalAccessException {

                setter.invoke(bean, longValue);
            }
        });
    }

    protected void setCharacterOption(final T bean, final String value) {

        final boolean empty = StringUtils.isEmpty(value);
        if (!empty && value.length() > 1) {
            final String error = String.format("%s is a character and cannot accept '%s'", getUniqueId(), value);
            throw new OptionsParseException(error);
        }

        final Character charValue = empty ? '\u0000' : value.charAt(0);
        set(bean, new Value<T>() {

            public void set(final T bean, final Method setter)
                    throws InvocationTargetException, IllegalAccessException {

                setter.invoke(bean, charValue);
            }
        });
    }

    protected void setFloatOption(final T bean, final String value) {

        final Float floatValue = StringUtils.isEmpty(value) ? 0.0f : Float.valueOf(value);
        set(bean, new Value<T>() {

            public void set(final T bean, final Method setter)
                    throws InvocationTargetException, IllegalAccessException {

                setter.invoke(bean, floatValue);
            }
        });
    }

    protected void setDoubleOption(final T bean, final String value) {

        final Double doubleValue = StringUtils.isEmpty(value) ? 0.0 : Double.valueOf(value);
        set(bean, new Value<T>() {

            public void set(final T bean, final Method setter)
                    throws InvocationTargetException, IllegalAccessException {

                setter.invoke(bean, doubleValue);
            }
        });
    }

    protected void setStringOption(final T bean, final String value) {

        set(bean, new Value<T>() {

            public void set(final T bean, final Method setter)
                    throws InvocationTargetException, IllegalAccessException {

                setter.invoke(bean, value);
            }
        });
    }

}