diff --git a/src/me/teridax/jcash/Main.java b/src/me/teridax/jcash/Main.java index a336bdc..637ac01 100644 --- a/src/me/teridax/jcash/Main.java +++ b/src/me/teridax/jcash/Main.java @@ -6,10 +6,7 @@ import me.teridax.jcash.gui.account.AccountController; import me.teridax.jcash.gui.login.LoginController; import me.teridax.jcash.lang.Locales; -import javax.imageio.ImageIO; import javax.swing.*; -import java.awt.*; -import java.io.IOException; import java.util.Objects; import java.util.logging.*; @@ -100,8 +97,6 @@ public final class Main { * Attempts to create a new instance of the singleton class Main. * This method throws an exception if the class is already instantiated. * @throws IllegalStateException if the class is already instantiated - * @pre the static instance of this class should be null. - * @post the static instance of this class will be set */ public static void instance() { if (null != instance) diff --git a/src/me/teridax/jcash/banking/accounts/Account.java b/src/me/teridax/jcash/banking/accounts/Account.java index 0949c0e..b17d1fa 100644 --- a/src/me/teridax/jcash/banking/accounts/Account.java +++ b/src/me/teridax/jcash/banking/accounts/Account.java @@ -43,8 +43,6 @@ public abstract class Account { Objects.requireNonNull(columns); Logging.LOGGER.finer("Parsing account from columns"); - Logging.LOGGER.finer("Decoding account fields"); - // deserialize fields var iban = StringDecoder.decodeUniqueIdentificationNumber(columns[0]); var pin = StringDecoder.decodeUniqueIdentificationNumber(columns[1]); diff --git a/src/me/teridax/jcash/decode/DecodeTests.java b/src/me/teridax/jcash/decode/DecodeTests.java new file mode 100644 index 0000000..f6e34d0 --- /dev/null +++ b/src/me/teridax/jcash/decode/DecodeTests.java @@ -0,0 +1,67 @@ +package me.teridax.jcash.decode; + +import me.teridax.jcash.lang.Locales; +import org.junit.Test; + +import static junit.framework.TestCase.assertEquals; +import static me.teridax.jcash.decode.StringDecoder.*; + +public class DecodeTests { + @Test + public void testDecodeSuccessfulFunctions() { + // make sure a comma separated integer from fractions + Locales.setDefaultLocale("de", "DE"); + + assertEquals(decodePercent("1,003"), 100.3d, 1e-3d); + + decodeUniqueIdentificationNumber("9578647895"); + decodeUniqueIdentificationNumber(" 927856347 "); + decodeUniqueIdentificationNumber("0"); + decodeUniqueIdentificationNumber("'9578647895"); + + 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.4, 1e-3d); + assertEquals(decodePercent("99"), 9900.0d); + assertEquals(decodePercent("1,003%"), 1.003, 1e-5d); + assertEquals(decodePercent("1,003"), 100.3, 1e-5d); + assertEquals(decodePercent("'1,003"), 100.3, 1e-5d); + + assertEquals(decodeCurrency("1,3€"), 1.3d); + assertEquals(decodeCurrency("‘0567€"), 567d); + assertEquals(decodeCurrency("145,34"), 145,34d); + assertEquals(decodeCurrency("0,45 €"), 0.45d); + } + + @Test + public void decodeLocaleDependent() { + Locales.setDefaultLocale("de", "DE"); + assertEquals(decodeCurrency("13.00,45€"), 1300.45); + + Locales.setDefaultLocale("en", "EN"); + assertEquals(decodeCurrency("13,00.45€"), 1300.45); + } + + @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"); + } +} diff --git a/src/me/teridax/jcash/decode/StringDecoder.java b/src/me/teridax/jcash/decode/StringDecoder.java index 860df04..11ddccd 100644 --- a/src/me/teridax/jcash/decode/StringDecoder.java +++ b/src/me/teridax/jcash/decode/StringDecoder.java @@ -1,33 +1,26 @@ package me.teridax.jcash.decode; -import org.junit.Test; +import me.teridax.jcash.lang.Locales; 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); + public static NumberFormat getNumberFormat() { + return NumberFormat.getInstance(Locales.getDefaultLocale()); + } /** * 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. + * The output value will be in the range [0, 100]. Strings formatted without a percentage + * symbol will be assumed to be normalized and thus multiplied by 100 to retrieve the result in percent. * @param number the string to convert * @return the double value * @throws IllegalArgumentException when the format is invalid @@ -36,19 +29,22 @@ public class StringDecoder { 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(); + // trim and cut out weird leading single quotes for numbers + var prepared = number.trim().replaceAll("^\\s*['‘`](?=\\d)", ""); var pattern = Pattern.compile("^([^%]+)?(%)?$", Pattern.CASE_INSENSITIVE); - var matcher = pattern.matcher(trimmed); + var matcher = pattern.matcher(prepared); if (matcher.find()) { + // if no percentage symbol is given the number will be multiplied by 100% var scale = 1e2; + // check if capture group 2 captured a percentage symbol + // if to we don't want to apply any scaling to the value if (null != matcher.group(2)) scale = 1; try { - return LOCAL_NUMBER_FORMAT.parse(matcher.group(1)).doubleValue() * scale; + return getNumberFormat().parse(matcher.group(1)).doubleValue() * scale; } catch (ParseException ex) { throw new IllegalArgumentException("Not a valid number: " + number, ex); } @@ -68,10 +64,10 @@ public class StringDecoder { Objects.requireNonNull(currency); // trim and cut out weird leading single quotes for numbers - var preparedUID = currency.trim().replaceAll("^\\s*['‘`](?=\\d)", ""); + var prepared = currency.trim().replaceAll("^\\s*['‘`](?=\\d)", ""); try { - return LOCAL_NUMBER_FORMAT.parse(preparedUID).doubleValue(); + return getNumberFormat().parse(prepared).doubleValue(); } catch (ParseException ex) { throw new IllegalArgumentException("Not a valid currency in german locale: " + currency, ex); } @@ -93,9 +89,7 @@ public class StringDecoder { // check if the string is a valid unsigned number try { - LOCAL_NUMBER_FORMAT.setParseIntegerOnly(true); - var serialNumber = LOCAL_NUMBER_FORMAT.parse(preparedUID); - LOCAL_NUMBER_FORMAT.setParseIntegerOnly(false); + var serialNumber = getNumberFormat().parse(preparedUID); if (serialNumber.intValue() < 0) throw new IllegalArgumentException("Not a valid unique identification number: " + number); @@ -146,47 +140,4 @@ public class StringDecoder { throw new IllegalArgumentException("not a void address"); } } - - @Test - public void testDecodeSuccessfulFunctions() { - assertEquals(decodePercent("1,003"), 100.3d, 1e-3d); - - decodeUniqueIdentificationNumber("9578647895"); - 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.4, 1e-3d); - assertEquals(decodePercent("99"), 9900.0d); - assertEquals(decodePercent("1,003%"), 1.003, 1e-5d); - assertEquals(decodePercent("1,003"), 100.3, 1e-5d); - - 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"); - } } diff --git a/src/me/teridax/jcash/gui/IconProvider.java b/src/me/teridax/jcash/gui/IconProvider.java index ea0e92b..b2e1b2b 100644 --- a/src/me/teridax/jcash/gui/IconProvider.java +++ b/src/me/teridax/jcash/gui/IconProvider.java @@ -1,7 +1,5 @@ package me.teridax.jcash.gui; -import me.teridax.jcash.Main; - import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; diff --git a/src/me/teridax/jcash/gui/account/AccountView.java b/src/me/teridax/jcash/gui/account/AccountView.java index f663098..700480d 100644 --- a/src/me/teridax/jcash/gui/account/AccountView.java +++ b/src/me/teridax/jcash/gui/account/AccountView.java @@ -49,12 +49,12 @@ public class AccountView extends JPanel { this.name.setText(owner.getName() + " " + owner.getFamilyName()); this.address.setText(owner.getStreet() + " " + owner.getCity()); - this.balance.setText(StringDecoder.LOCAL_NUMBER_FORMAT.format(account.getBalance()) + " €"); + this.balance.setText(StringDecoder.getNumberFormat().format(account.getBalance()) + " €"); this.type.setText(translate(account.getClass().getSimpleName())); - if (account instanceof CurrentAccount) {; + if (account instanceof CurrentAccount) { this.typeSpecialLabel.setText(translate("Overdraft")); - this.typeSpecialProperty.setText( StringDecoder.LOCAL_NUMBER_FORMAT.format(((CurrentAccount) account).getOverdraft()) + " €"); + this.typeSpecialProperty.setText( StringDecoder.getNumberFormat().format(((CurrentAccount) account).getOverdraft()) + " €"); } else if (account instanceof SavingsAccount) { this.typeSpecialLabel.setText(translate("Interest rate")); this.typeSpecialProperty.setText( ((SavingsAccount) account).getInterest() + " %" ); diff --git a/src/me/teridax/jcash/gui/deposit/DepositView.java b/src/me/teridax/jcash/gui/deposit/DepositView.java index b496001..6683e4b 100644 --- a/src/me/teridax/jcash/gui/deposit/DepositView.java +++ b/src/me/teridax/jcash/gui/deposit/DepositView.java @@ -87,7 +87,7 @@ public class DepositView { this.cancel = new JButton(translate("Cancel")); this.deposit = new JButton(translate("Deposit")); - this.value = new JFormattedTextField(StringDecoder.LOCAL_NUMBER_FORMAT); + this.value = new JFormattedTextField(StringDecoder.getNumberFormat()); this.dialog.setContentPane(new JPanel(new GridBagLayout())); } diff --git a/src/me/teridax/jcash/gui/takeoff/TakeoffView.java b/src/me/teridax/jcash/gui/takeoff/TakeoffView.java index 8fa6f5e..62f101b 100644 --- a/src/me/teridax/jcash/gui/takeoff/TakeoffView.java +++ b/src/me/teridax/jcash/gui/takeoff/TakeoffView.java @@ -86,7 +86,7 @@ public class TakeoffView { this.cancel = new JButton(translate("Cancel")); this.takeoff = new JButton(translate("Takeoff")); - this.value = new JFormattedTextField(StringDecoder.LOCAL_NUMBER_FORMAT); + this.value = new JFormattedTextField(StringDecoder.getNumberFormat()); this.dialog.setContentPane(new JPanel(new GridBagLayout())); } diff --git a/src/me/teridax/jcash/gui/transfer/TransferView.java b/src/me/teridax/jcash/gui/transfer/TransferView.java index 82e9cc9..72b0d7b 100644 --- a/src/me/teridax/jcash/gui/transfer/TransferView.java +++ b/src/me/teridax/jcash/gui/transfer/TransferView.java @@ -109,7 +109,7 @@ public class TransferView { this.cancel = new JButton(translate("Cancel")); this.transfer = new JButton(translate("Transfer")); - this.value = new JFormattedTextField(StringDecoder.LOCAL_NUMBER_FORMAT); + this.value = new JFormattedTextField(StringDecoder.getNumberFormat()); this.iban = new JFormattedTextField(); this.blz = new JFormattedTextField(); diff --git a/src/me/teridax/jcash/lang/Locales.java b/src/me/teridax/jcash/lang/Locales.java index 7739f6e..5157ca2 100644 --- a/src/me/teridax/jcash/lang/Locales.java +++ b/src/me/teridax/jcash/lang/Locales.java @@ -1,5 +1,7 @@ package me.teridax.jcash.lang; +import me.teridax.jcash.Logging; + import java.util.Locale; /** @@ -8,10 +10,7 @@ import java.util.Locale; @SuppressWarnings("unused") public class Locales { - /** - * Locale string used to identify a certain locale - */ - private static String defaultLocale = "en_EN"; + private static Locale defaultLocale = new Locale("en", "EN"); /** * Sets the default locale to use for the application instance. @@ -24,13 +23,14 @@ public class Locales { var locale = language + "_" + country; if (Translator.setTranslationLocale(locale)) { + defaultLocale = new Locale(language, country); // apply locale to JVM - Locale.setDefault(new Locale(language, country)); - defaultLocale = locale; + Locale.setDefault(defaultLocale); } + Logging.LOGGER.info("Using locale: " + locale); } - public static String getDefaultLocale() { + public static Locale getDefaultLocale() { return defaultLocale; }