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.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); } }); } }