package uk.ac.ox.zoo.seeg.abraid.mp.common.util; import com.fasterxml.jackson.databind.MappingIterator; import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.dataformat.csv.CsvMapper; import com.fasterxml.jackson.dataformat.csv.CsvSchema; import org.springframework.util.StringUtils; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * Utilities for parsing. * * Copyright (c) 2014 University of Oxford */ public final class ParseUtils { private ParseUtils() { } /** * Parses a string into an integer. Trims whitespace and does not throw a NumberFormatException. * @param toParse The string to parse. * @return The parsed integer, or null if it could not be parsed. */ public static Integer parseInteger(String toParse) { try { return Integer.parseInt(convertString(toParse)); } catch (NumberFormatException e) { return null; } } /** * Parses a string into a long. Trims whitespace and does not throw a NumberFormatException. * @param toParse The string to parse. * @return The parsed long, or null if it could not be parsed. */ public static Long parseLong(String toParse) { try { return Long.parseLong(convertString(toParse)); } catch (NumberFormatException e) { return null; } } /** * Parses a string into a double. Trims whitespace and does not throw a NumberFormatException. * @param toParse The string to parse. * @return The parsed double, or null if it could not be parsed. */ public static Double parseDouble(String toParse) { String convertedString = convertString(toParse); // Null input causes Double.parseDouble to throw a NullPointerException, so handle this separately if (convertedString == null) { return null; } try { return Double.parseDouble(convertedString); } catch (NumberFormatException e) { return null; } } /** * Converts a string to a canonical form, by stripping whitespace and converting to a null if an empty string. * @param toConvert The string to convert. * @return The converted string. */ public static String convertString(String toConvert) { String convertedString = StringUtils.trimWhitespace(toConvert); if (convertedString != null && convertedString.equals("")) { convertedString = null; } return convertedString; } /** * Parses a list of strings into a list of integers using parseInteger(). * @param strings A list of strings to parse. * @return The parsed integers. */ public static List<Integer> parseIntegers(List<String> strings) { List<Integer> parsedIntegers = null; if (strings != null) { parsedIntegers = new ArrayList<>(); for (String string : strings) { parsedIntegers.add(ParseUtils.parseInteger(string)); } } return parsedIntegers; } /** * Converts a list of strings using convertString(). * @param strings A list of strings to convert. * @return The converted strings. */ public static List<String> convertStrings(List<String> strings) { List<String> convertedStrings = null; if (strings != null) { convertedStrings = new ArrayList<>(); for (String string : strings) { convertedStrings.add(ParseUtils.convertString(string)); } } return convertedStrings; } /** * Splits the specified comma-delimited text. All tokens are trimmed and empty tokens are ignored. * @param text The text to split. * @return The split text. */ public static List<String> splitCommaDelimitedString(String text) { List<String> splitList = new ArrayList<>(); if (StringUtils.hasText(text)) { // Note: all tokens are trimmed and empty tokens are ignored String[] splitArray = StringUtils.tokenizeToStringArray(text, ",", true, true); Collections.addAll(splitList, splitArray); } return splitList; } /** * Splits the specified space-delimited text. All tokens are trimmed and empty tokens are ignored. * @param text The text to split. * @return The split text. */ public static List<String> splitSpaceDelimitedString(String text) { return Arrays.asList(StringUtils.tokenizeToStringArray(text, " ", true, true)); } /** * Reads a CSV file into a list of the specified type. * @param csv The CSV file. * @param responseClass The type of the returned list's elements. * @param schema A schema representing the CSV file's structure. * @param <T> The type of the returned list's elements. * @return A list of parsed elements. * @throws IOException if parsing failed. */ public static <T> List<T> readFromCsv(String csv, Class<T> responseClass, CsvSchema schema) throws IOException { if (csv == null) { // Protect against NullPointerException csv = ""; } ObjectReader reader = new CsvMapper().reader(responseClass).with(schema); MappingIterator<T> iterator = reader.readValues(csv); ArrayList<T> results = new ArrayList<>(); try { while (iterator.hasNext()) { results.add(iterator.next()); } } catch (RuntimeException e) { // ObjectReader throws (subclasses of) IOException, but MappingIterator wraps them in a RuntimeException. // We unwrap them for consistency. Throwable cause = e.getCause(); if (cause != null && cause instanceof IOException) { throw (IOException) cause; } throw e; } return results; } }