candr.yoclip.option.AbstractSetterOption.java Source code

Java tutorial

Introduction

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

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

import candr.yoclip.OptionsParseException;
import candr.yoclip.ParserOption;

/**
 * Used by the parser to associate an {@link candr.yoclip.annotation.Option Option},
 * {@link candr.yoclip.annotation.OptionProperties OptionProperties}, or {@link candr.yoclip.annotation.Arguments Arguments} annotation with a
 * {@code Method} in a class that contains option parser annotations.
 */
abstract class AbstractSetterOption<T> implements ParserOption<T> {
    /**
     * The method that has an options annotation associated with it.
     */
    private Method setter;

    /**
     * Initializes the setter descriptor to create an association between the setter {@code Method} and the {@code Option}
     * annotation.
     *
     * @param setter The setter method annotated with {@code Option}.
     * @throws UnsupportedOperationException if the {@code Method} does not follow the bean setter pattern and the single parameter
     *                                       is not type String.
     */
    public AbstractSetterOption(final Method setter) {
        if (null == setter) {
            throw new IllegalArgumentException("Setter method cannot be null.");
        }
        this.setter = setter;
    }

    /**
     * Get the Java {@code Method} that is annotated with either an {@link candr.yoclip.annotation.Option Option},
     * {@link candr.yoclip.annotation.OptionProperties}, or {@link candr.yoclip.annotation.Arguments Arguments} annotation.
     *
     * @return the Java {@code Method} that has an option annotation associated with it.
     */
    public Method getSetter() {
        return setter;
    }

    /**
     * Get the {@code Class} object that identifies the parameter type of the setter {@code Method} parameter.
     *
     * @return the {@code Class} that identifies the parameter type of the setter {@code Method}.
     */
    public Class<?> getType() {
        return getSetter().getParameterTypes()[0];
    }

    /**
     * The {@code Class} object representing the class or interface that declares the setter {@code Method}.
     *
     * @return the {@code Class} that declares the setter {@code Method}.
     */
    @Override
    public Class<?> getDeclaringClass() {
        return getSetter().getDeclaringClass();
    }

    /**
     * A default unique id for the setter {@code Method}. Typically this will be the string returned by the {@link Method#toGenericString()}
     * method.
     *
     * @return the unique id for the setter.
     */
    @Override
    public String getUniqueId() {
        return getSetter().getName();
    }

    /**
     * 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 false;
    }

    /**
     * Indicates the field is annotated with a {@link candr.yoclip.annotation.OptionProperties OptionProperties} annotation.
     *
     * @return {@code true} if the annotation is {@code OptionProperties}, {@code false} otherwise.
     */
    @Override
    public boolean isProperties() {

        return false;
    }

    /**
     * Indicates the field is annotated with a {@link candr.yoclip.annotation.Arguments Arguments} annotation.
     *
     * @return {@code true} if the annotation is {@code Arguments}, {@code false} otherwise.
     */
    @Override
    public boolean isArguments() {

        return false;
    }

    /**
     * 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 false;
    }

    @Override
    public List<Pair<String, String>> getPropertyDescriptions() {
        return Collections.emptyList();
    }

    protected void set(final T bean, final Value<T> value) {

        final Method setter = getSetter();

        boolean resetFieldAccessibility = false;
        try {

            if (!setter.isAccessible()) {
                setter.setAccessible(true);
                resetFieldAccessibility = true;
            }

            value.set(bean, setter);

        } catch (Exception e) {
            if (e instanceof OptionsParseException) {
                throw (OptionsParseException) e;
            }
            throw new OptionsParseException("Error setting value " + getUniqueId(), e);

        } finally {
            if (resetFieldAccessibility) {
                setter.setAccessible(false);
            }
        }
    }

    @Override
    public String toString() {
        return getUniqueId();
    }

    /**
     * Used internally to set a value. Callers use the interface similar to a poor mans closure. The setters
     * framework ensures a normally inaccessible method is settable before calling {@link Method#invoke(Object, Object...)} on the method.
     *
     * @param <T> The bean type that will be updated.
     */
    public interface Value<T> {

        /**
         * Called by the framework when a value should be set into a bean object.
         *
         * @param bean   The object whose field will be set.
         * @param setter The field in the object that will be set.
         */
        void set(T bean, Method setter) throws InvocationTargetException, IllegalAccessException;
    }

}