package org.caudexorigo.cli; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; class ArgumentTyperImpl<O> implements ArgumentTyper<O> { private final ValidationErrorBuilder m_validationErrorBuilder; private final OptionsSpecification<O> m_specification; public ArgumentTyperImpl(final OptionsSpecification<O> specification) { m_validationErrorBuilder = new ValidationErrorBuilderImpl(); m_specification = specification; } /** * @inheritdoc */ public TypedArguments typeArguments(final ValidatedArguments validatedArguments) throws ArgumentValidationException { final TypedArgumentsImpl typedArguments = typeParsedArguments(validatedArguments); typeUnparsedArguments(validatedArguments, typedArguments); m_validationErrorBuilder.validate(); return typedArguments; } private void typeUnparsedArguments(final ValidatedArguments validatedArguments, final TypedArgumentsImpl typedArguments) { if (m_specification.hasUnparsedSpecification()) { final ArgumentSpecification specification = m_specification.getUnparsedSpecification(); typedArguments.setUnparsedValue(getValue(specification.getMethod(), validatedArguments.getUnparsed(), specification)); } } private TypedArgumentsImpl typeParsedArguments(final ValidatedArguments validatedArguments) { final TypedArgumentsImpl typedArguments = new TypedArgumentsImpl(); for (final OptionSpecification optionSpecification : m_specification) { if (validatedArguments.containsAny(optionSpecification.getAllNames()) || optionSpecification.hasDefaultValue()) { final Object value; if (optionSpecification.hasValue()) { value = getValue(validatedArguments, optionSpecification.getMethod(), optionSpecification); } else { value = Boolean.TRUE; } typedArguments.add(optionSpecification, value); } } return typedArguments; } private Object getValue(final ValidatedArguments arguments, final Method method, final OptionSpecification specification) { final List<String> values = arguments.containsAny(specification.getAllNames()) ? arguments.getValues(specification.getAllNames()) : specification.getDefaultValue(); return getValue(method, values, specification); } @SuppressWarnings("unchecked") private Object getValue(final Method method, final List<String> values, final ArgumentSpecification specification) { try { final Class<?> type = specification.getType(); final List<Object> result = new ArrayList<Object>(); for (final String value : values) { if (type.isEnum()) { result.add(Enum.valueOf((Class<? extends Enum>) type, values.get(0))); } else if (type.isPrimitive()) { if (type.equals(Character.TYPE) || type.equals(Character.class)) { result.add(value.charAt(0)); } else if (type.equals(Byte.TYPE) || type.equals(Short.class)) { result.add(Byte.parseByte(value)); } else if (type.equals(Short.TYPE) || type.equals(Short.class)) { result.add(Short.parseShort(value)); } else if (type.equals(Integer.TYPE) || type.equals(Integer.class)) { result.add(Integer.parseInt(value)); } else if (type.equals(Long.TYPE) || type.equals(Long.class)) { result.add(Long.parseLong(value)); } else if (type.equals(Float.TYPE) || type.equals(Float.class)) { result.add(Float.parseFloat(value)); } else if (type.equals(Double.TYPE) || type.equals(Double.class)) { result.add(Double.parseDouble(value)); } else { throw new UnsupportedOperationException(String.format("Method (%s) return type not supported for reading argument values", method.toGenericString())); } } else if (type.equals(Character.class)) { // there is always an exception, and java.lang.Character can't constructed from a string if (value.length() == 1) { result.add(value.charAt(0)); } else { throw new ValueFormatException(String.format("value is not a character (%s)", value)); } } else { final Constructor<?> constructor = type.getConstructor(new Class[] { String.class }); result.add(constructor.newInstance(new Object[] { value })); } } if (specification.isMultiValued()) { return result; } else { return result.get(0); } } catch (final NumberFormatException e) { m_validationErrorBuilder.invalidValueForType(specification, unsupportedNumberFormatMessage(e)); } catch (final NoSuchMethodException e) { m_validationErrorBuilder.unableToConstructType(specification, e.getMessage()); } catch (final InstantiationException e) { m_validationErrorBuilder.unableToConstructType(specification, e.getMessage()); } catch (final IllegalAccessException e) { m_validationErrorBuilder.unableToConstructType(specification, e.getMessage()); } catch (final InvocationTargetException e) { final Throwable cause = e.getCause(); if (cause instanceof NumberFormatException) { m_validationErrorBuilder.invalidValueForType(specification, unsupportedNumberFormatMessage((NumberFormatException) cause)); } else { m_validationErrorBuilder.invalidValueForType(specification, cause.getMessage()); } } catch (final ValueFormatException e) { m_validationErrorBuilder.invalidValueForType(specification, e.getMessage()); } return null; } private String unsupportedNumberFormatMessage(final NumberFormatException e1) { return "Unsupported number format: " + e1.getMessage(); } }