package io.katharsis.utils.parser; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.math.BigDecimal; import java.math.BigInteger; import java.util.LinkedList; import java.util.List; /** * Parses {@link String} into an instance of provided {@link Class}. It support the following classes: * <ol> * <li>{@link String}</li> * <li>{@link Byte} and <i>byte</i></li> * <li>{@link Short} and <i>short</i></li> * <li>{@link Integer} and <i>int</i></li> * <li>{@link Long} and <i>long</i></li> * <li>{@link Float} and <i>float</i></li> * <li>{@link Double} and <i>double</i></li> * <li>{@link BigInteger}</li> * <li>{@link BigDecimal}</li> * <li>{@link Character} and <i>char</i></li> * <li>{@link Boolean} and <i>boolean</i></li> * <li>{@link java.util.UUID}</li> * <li>An {@link Enum}</li> * <li>A class with a {@link String} only constructor</li> * </ol> */ public class TypeParser { /** * Parses an {@link Iterable} of String instances to {@link Iterable} of parsed values. * @param inputs list of Strings * @param clazz type to be parsed to * @param <T> type of class * @return {@link Iterable} of parsed values */ public <T extends Serializable> Iterable<T> parse(Iterable<String> inputs, Class<T> clazz) { List<T> parsedValues = new LinkedList<>(); for (String input : inputs) { parsedValues.add(parse(input, clazz)); } return parsedValues; } /** * Parses a {@link String} to an instance of passed {@link Class} * @param input String value * @param clazz type to be parsed to * @param <T> type of class * @return instance of parsed value */ public <T extends Serializable> T parse(String input, Class<T> clazz) { try { return parseInput(input, clazz); } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException | NumberFormatException | ParserException e) { throw new ParserException(e.getMessage()); } } @SuppressWarnings("unchecked") private <T extends Serializable> T parseInput(String input, Class<T> clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { T parsedValue; if (String.class.equals(clazz)) { parsedValue = (T) input; } else if (StandardTypeParsers.parsers.containsKey(clazz)) { StandardTypeParser standardTypeParser = StandardTypeParsers.parsers.get(clazz); parsedValue = (T) standardTypeParser.parse(input); } else if (isEnum(clazz)) { parsedValue = (T) Enum.valueOf((Class<Enum>)clazz.asSubclass(Enum.class), input.trim()); } else if (containsStringConstructor(clazz)) { parsedValue = clazz.getDeclaredConstructor(String.class).newInstance(input); } else { throw new ParserException(String.format("Cannot parse to %s : %s", clazz.getName(), input)); } return parsedValue; } private static <T extends Serializable> boolean isEnum(Class<T> clazz) { return clazz.isEnum(); } private boolean containsStringConstructor(Class<?> clazz) throws NoSuchMethodException { boolean result = false; for (Constructor constructor : clazz.getDeclaredConstructors()) { if (constructor.getParameterTypes().length == 1 && constructor.getParameterTypes()[0] == String.class) { result = true; } } return result; } }