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