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