candr.yoclip.option.OptionField.java Source code

Java tutorial

Introduction

Here is the source code for candr.yoclip.option.OptionField.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.Field;
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 candr.yoclip.annotation.Option Option} annotation with a Java {@code Field} in a class that
 * contains option parser annotations.
 */
class OptionField<T> extends AbstractFieldOption<T> implements ParserOption<T> {

    /**
     * The {@link candr.yoclip.annotation.Option} annotation associated with a field.
     */
    final private Option option;

    /**
     * Initializes the field descriptor to create an association between the bean {@code Field} and the {@code Option}
     * annotation.
     *
     * @param option The annotation associated with the bean field.
     * @param field  The bean field annotated with {@code Option}.
     * @throws candr.yoclip.OptionsBadNameException if one of the option {@link candr.yoclip.annotation.Option#name()
     *                                              names} is empty.
     * @throws candr.yoclip.OptionsBadTypeException if the field does not have any names defined and the field type is
     *                                              not assignable to the Java {@code List} interface.
     */
    public OptionField(final Option option, final Field field) {

        super(field);

        if (option.name().length == 0) {
            throw new OptionsBadNameException("Options must have at least one name.");
        }

        for (final String name : option.name()) {
            if (StringUtils.isEmpty(name)) {
                throw new OptionsBadNameException(field.getName() + " has an option name that is empty.");
            }
        }

        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 candr.yoclip.annotation.Option#usage() Option} {@code usage} field. Please refer to the
     * {@link 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 candr.yoclip.annotation.Option#description() Option} {@code description} field. Please refer to
     * the {@link 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 candr.yoclip.annotation.Option#required() Option} {@code 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() {

        boolean hasValue = false;
        // allow a boolean to be explicitly set true or false
        if (Boolean.TYPE.equals(getField().getType()) || Boolean.class.isAssignableFrom(getField().getType())) {
            hasValue = option.hasValue();

        } else if (option.name().length > 0) {
            // arguments do not have names
            hasValue = true;
        }
        return 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<?> fieldType = getField().getType();
        try {

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

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

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

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

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

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

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

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

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

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

        } catch (NumberFormatException e) {
            final String error = String.format("Error converting '%s' to %s", value, fieldType);
            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 Field field) throws IllegalAccessException {

                field.set(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 Field field) throws IllegalAccessException {

                field.set(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 Field field) throws IllegalAccessException {

                field.set(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 Field field) throws IllegalAccessException {

                field.set(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 Field field) throws IllegalAccessException {

                field.set(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'", getField().getName(),
                    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 Field field) throws IllegalAccessException {

                field.set(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 Field field) throws IllegalAccessException {

                field.set(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 Field field) throws IllegalAccessException {

                field.set(bean, doubleValue);
            }
        });
    }

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

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

            public void set(final T bean, final Field field) throws IllegalAccessException {

                field.set(bean, value);
            }
        });
    }

}