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.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; 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 candr.yoclip.annotation.OptionProperties OptionProperties} annotation with a Java {@code Field} * in a class that contains option parser annotations. */ class OptionPropertiesField<T> extends AbstractFieldOption<T> implements ParserOption<T> { /** * The {@link candr.yoclip.annotation.OptionProperties} annotation associated with a field. */ final private OptionProperties optionProperties; /** * Initializes the field descriptor to create an association between the bean {@code Field} and the * {@code OptionProperties} annotation. * * @param optionProperties The annotation associated with the bean field. * @param field The bean field annotated with {@code OptionProperties}. * @throws candr.yoclip.OptionsBadTypeException if the field is not assignable to the Java {@code Map} interface. * @throws candr.yoclip.OptionsBadNameException if the {@link candr.yoclip.annotation.OptionProperties#name() name} * is empty. */ protected OptionPropertiesField(final OptionProperties optionProperties, final Field field) { super(field); if (!Map.class.isAssignableFrom(field.getType())) { throw new OptionsBadTypeException(field.getName() + " must be a Map"); } if (StringUtils.isEmpty(optionProperties.name())) { throw new OptionsBadNameException(field.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 candr.yoclip.annotation.OptionProperties#usage() OptionProperties} {@code usage} field. 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 candr.yoclip.annotation.OptionProperties#description() OptionProperties} {@code description} * field. 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 candr.yoclip.annotation.OptionProperties#required() OptionProperties} {@code required} field. * * @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 field is annotated with a {@link candr.yoclip.annotation.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>() { @Override public void set(final T bean, final Field field) throws IllegalAccessException { try { final Method argumentsSetter = field.getType().getMethod("put", Object.class, Object.class); final Object propertiesMap = field.get(bean); if (null == propertiesMap) { throw new OptionsParseException(field.getName() + " is null and cannot be set..."); } argumentsSetter.invoke(propertiesMap, keyAndValue[0], keyAndValue[1]); } catch (final NoSuchMethodException e) { throw new OptionsParseException("Yikes!!! Could not get Map.put(key,value) method...", e); } catch (final InvocationTargetException e) { throw new OptionsParseException("Yikes!!! Map.put(key,value) threw an exception...", e); } } }); } }