candr.yoclip.option.OptionPropertiesSetter.java Source code

Java tutorial

Introduction

Here is the source code for candr.yoclip.option.OptionPropertiesSetter.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.Collections;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;

import candr.yoclip.*;
import candr.yoclip.annotation.OptionProperties;
import candr.yoclip.annotation.PropertyDescription;

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

    /**
     * The {@link OptionProperties} annotation associated with a setter.
     */
    final private OptionProperties optionProperties;

    /**
     * Initializes the method descriptor to create an association between the bean setter {@code Method} and the
     * {@code OptionProperties} annotation.
     *
     * @param optionProperties The annotation associated with the bean setter.
     * @param setter           The bean setter method annotated with {@code OptionProperties}.
     * @throws OptionsBadTypeException if the setter is not assignable to the Java {@code Map} interface.
     * @throws OptionsBadNameException if the {@link OptionProperties#name() name} is empty.
     */
    protected OptionPropertiesSetter(final OptionProperties optionProperties, final Method setter) {

        super(setter);

        final Class<?>[] parameterTypes = setter.getParameterTypes();
        if (2 != parameterTypes.length) {
            throw new UnsupportedOperationException(setter.toGenericString() + " must have 2 parameters.");
        }
        if (!String.class.isAssignableFrom(parameterTypes[0])
                || !String.class.isAssignableFrom(parameterTypes[1])) {
            throw new IllegalArgumentException(setter.toGenericString() + " parameters must both be String");
        }
        if (StringUtils.isEmpty(optionProperties.name())) {
            throw new OptionsBadNameException(setter.getName() + " option name is empty.");
        }

        this.optionProperties = optionProperties;
    }

    /**
     * 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 new String[] { getOptionProperties().name() };
    }

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

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

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

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

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

        return getOptionProperties().required();
    }

    /**
     * The bean accessor for the option properties annotation.
     *
     * @return the option properties annotation.
     */
    protected OptionProperties getOptionProperties() {
        return optionProperties;
    }

    /**
     * Indicates the setter is annotated with a {@link OptionProperties OptionProperties}
     * annotation.
     *
     * @return Always returns {@code true}.
     */
    @Override
    public boolean isProperties() {

        return true;
    }

    @Override
    public List<Pair<String, String>> getPropertyDescriptions() {

        final PropertyDescription[] propertyDescriptions = getOptionProperties().propertyDescriptions();

        if (ArrayUtils.isEmpty(propertyDescriptions)) {
            return Collections.emptyList();
        }

        List<Pair<String, String>> synopsisAndDetails = new LinkedList<Pair<String, String>>();
        for (final PropertyDescription propertyDescription : propertyDescriptions) {
            synopsisAndDetails.add(
                    Pair.of(propertyDescription.synopsis(), OptionUtils.combine(propertyDescription.details())));
        }

        return synopsisAndDetails;
    }

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

        // this should never happen unless the property pattern matcher changes
        final String[] keyAndValue = StringUtils.split(value, OptionProperties.KEY_VALUE_SEPARATOR);
        if (keyAndValue.length != 2) {
            throw new OptionsParseException("Yikes!!! Expected key=value not '" + value + "'...");
        }

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

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

                setter.invoke(bean, keyAndValue[0], keyAndValue[1]);
            }
        });
    }
}