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