package me.teridax.jcash.decode; import org.junit.Test; import java.text.NumberFormat; import java.text.ParseException; import java.util.Locale; import java.util.Objects; import java.util.regex.Pattern; import static junit.framework.TestCase.assertEquals; /** * Utility class for converting various single line strings into a specific data type according * to a format mostly dictated by a locale. */ public class StringDecoder { /** * Locale to use when converting strings */ private static final Locale LOCALE = Locale.GERMANY; /** * NumberFormat to use when converting strings */ public static final NumberFormat LOCAL_NUMBER_FORMAT = NumberFormat.getInstance(LOCALE); /** * Attempts to convert the given string into a double value representing a percentage. * The percentage is stored in the range [0,1] and can linearly be mapped to [0, 100] by multiplying with 100. * @param number the string to convert * @return the double value * @throws IllegalArgumentException when the format is invalid * @throws NullPointerException when the argument is null */ public static double decodePercent(String number) throws IllegalArgumentException, NullPointerException { Objects.requireNonNull(number); // trim the number and cut out optional percent symbols var trimmed = number.trim().replace("%", ""); try { return LOCAL_NUMBER_FORMAT.parse(trimmed).doubleValue(); } catch (ParseException ex) { throw new IllegalArgumentException("Not a valid number: " + number, ex); } } /** * Attempts to convert the given string into a currency value. * @param currency the string to convert * @return the double value * @throws IllegalArgumentException when the format is invalid * @throws NullPointerException when the argument is null */ public static double decodeCurrency(String currency) throws IllegalArgumentException, NullPointerException { Objects.requireNonNull(currency); try { return LOCAL_NUMBER_FORMAT.parse(currency.trim()).doubleValue(); } catch (ParseException ex) { throw new IllegalArgumentException("Not a valid currency in german locale: " + currency, ex); } } /** * Attempts to convert the given string into universally unique number. * This function does not check for duplicates. The number must be a positive integer. * @param number the string to convert * @return the integer serial number * @throws IllegalArgumentException when the format is invalid * @throws NullPointerException when the argument is null */ public static int decodeUniqueIdentificationNumber(String number) throws IllegalArgumentException, NullPointerException { Objects.requireNonNull(number); // check if the string is a valid unsigned number try { LOCAL_NUMBER_FORMAT.setParseIntegerOnly(true); var serialNumber = LOCAL_NUMBER_FORMAT.parse(number.trim()); LOCAL_NUMBER_FORMAT.setParseIntegerOnly(false); if (serialNumber.intValue() < 0) throw new IllegalArgumentException("Not a valid unique identification number: " + number); return serialNumber.intValue(); } catch (ParseException ex) { throw new IllegalArgumentException("Not a valid serial number: " + number, ex); } } /** * Attempts to convert the given string into a name. * This method performs validation and trimming. * @param name the string to convert * @return the qualified name * @throws IllegalArgumentException when the format is invalid * @throws NullPointerException when the argument is null */ public static String decodeName(String name) throws IllegalArgumentException, NullPointerException { Objects.requireNonNull(name); var trimmed = name.trim(); var pattern = Pattern.compile("[\\w-\\s]+", Pattern.CASE_INSENSITIVE); var matcher = pattern.matcher(trimmed); if (matcher.find()) { return matcher.group(); } else { throw new IllegalArgumentException("not a void name"); } } /** * Attempts to convert the given string into a street and an optional house address. * @param street the string to convert * @return the address name * @throws IllegalArgumentException when the format is invalid * @throws NullPointerException when the argument is null */ public static String decodeStreet(String street) throws IllegalArgumentException, NullPointerException { Objects.requireNonNull(street); var pattern = Pattern.compile("\\S+(\\s+\\d+(/\\d+)?)?", Pattern.CASE_INSENSITIVE); var matcher = pattern.matcher(street); if (matcher.find()) { return matcher.group(); } else { throw new IllegalArgumentException("not a void address"); } } @Test public void testDecodeSuccessfulFunctions() { decodeUniqueIdentificationNumber("95786978625347895"); decodeUniqueIdentificationNumber(" 927856347 "); decodeUniqueIdentificationNumber("0"); decodeName("Adolf"); decodeName("Günther"); decodeName("Saßkia"); decodeStreet("Bahnhofstraße 40/1"); decodeStreet("Gülleweg 9"); decodeStreet("Echsengaße 67 / 4"); assertEquals(decodePercent("1,4%"), 1.4d); assertEquals(decodePercent("99"), 99.0d); assertEquals(decodePercent("1,003 %"), 1.003d); assertEquals(decodeCurrency("1,3€"), 1.3d); assertEquals(decodeCurrency("145,34"), 145,34d); assertEquals(decodeCurrency("0,45 €"), 0.45d); } @Test(expected = IllegalArgumentException.class) public void testDecodeInvalidFunctions() { decodeUniqueIdentificationNumber("q0948tvb6q047t 740 t74z0tz 784"); decodeUniqueIdentificationNumber("-39867.8475"); decodeUniqueIdentificationNumber("-398678475"); decodeUniqueIdentificationNumber(" ß9qu908t76q34798t6q734vb9843"); decodeName("John Doe"); decodeName("3490qt67v 0b34"); decodeName("Alexander9"); decodeName("-ga76re78g6$§"); decodeStreet("Bahnhofstraße -40/1"); decodeStreet("Gülleweg 9//567"); decodeStreet("23Echsengaße 67 / 4 Hofwetg 9"); } }