· 5 years ago · Oct 02, 2020, 11:22 PM
1package banking;
2
3import java.io.FileInputStream;
4import java.io.IOException;
5import java.sql.*;
6import java.text.DecimalFormat;
7import java.util.*;
8import java.util.logging.LogManager;
9import java.util.logging.Logger;
10
11import static java.lang.String.format;
12
13enum MainState {
14 MAIN,
15 ACCOUNT,
16 CREATE_ACCOUNT
17}
18
19class Account {
20 private static Random random = new Random();
21 private static DecimalFormat pinFormater = new DecimalFormat("###0");
22 private static String accountPattern = "^400000\\d{9,12}$";
23 private String cardNumber, pin;
24 private int id;
25 private int balance;
26
27 int getId() {
28 return id;
29 }
30
31 Account(int id, String cardNumber, String pin, int balance) {
32 this.id = id;
33 this.cardNumber = cardNumber;
34 this.pin = pin;
35 this.balance = balance;
36 }
37
38 static String generatePin() {
39 int pin = random.nextInt(9000) + 1000;
40 return pinFormater.format(pin);
41 }
42
43 static String getCardNumber(String accountNumber) {
44 if (!accountNumber.matches(accountPattern)) {
45 System.out.println("Wrong account number provided");
46 return null;
47 }
48 int sum = 0;
49 char[] chars = accountNumber.toCharArray();
50 for (int i = 0; i < chars.length; i++) {
51 int val = Integer.parseInt(String.valueOf(chars[i]));
52
53 if (i % 2 == 0) {
54 val *= 2;
55 }
56 if (val > 9) {
57 val -= 9;
58 }
59 sum += val;
60 }
61 return accountNumber + (10 - (sum % 10)) % 10;
62 }
63
64 static boolean verifyCardNumber(String accountNumber) {
65 String checkedNumber = getCardNumber(accountNumber.substring(0, accountNumber.length() - 1));
66 return accountNumber.equals(checkedNumber);
67 }
68
69 String getCardNumber() {
70 return cardNumber;
71 }
72
73 int getBalance() {
74 return balance;
75 }
76
77 boolean verifyPin(String pin) {
78 return this.pin.equals(pin);
79 }
80}
81
82class AccountConnection {
83 private final String url;
84 private final Connection conn;
85
86 public AccountConnection(String fileName) {
87 url = "jdbc:sqlite:" + fileName;
88 conn = connect();
89 createTable();
90 }
91
92 public void close() throws SQLException {
93 this.conn.close();
94 }
95
96 private Connection connect() {
97 Connection conn = null;
98 try {
99 conn = DriverManager.getConnection(url);
100 } catch (SQLException e) {
101 System.out.println(e.getMessage());
102 }
103 return conn;
104 }
105
106 private void createTable() {
107 String query = "CREATE TABLE IF NOT EXISTS card (\n" +
108 "id INTEGER,\n" +
109 "number TEXT,\n" +
110 "pin TEXT,\n" +
111 "balance INTEGER DEFAULT 0\n" +
112 ");";
113 boolean result = executeQuery(query);
114 }
115
116 private boolean executeQuery(String query) {
117 try {
118 Statement stmt = conn.createStatement();
119 return stmt.execute(query);
120 } catch (SQLException e) {
121 System.out.println(e.getMessage());
122 }
123 return false;
124 }
125
126 String getNextAccountNumber() throws SQLException {
127 String query = "SELECT CAST(SUBSTR(MAX(number), 0, 16) AS integer) + 1 as next from card;";
128 Statement stmt = conn.createStatement();
129 ResultSet result = stmt.executeQuery(query);
130
131 try {
132 result.next();
133 String accountNumber = result.getString("next");
134 return accountNumber == null ? "400000100000000" : accountNumber;
135 } catch (SQLException throwables) {
136 throwables.printStackTrace();
137 }
138 return null;
139 }
140
141 int getNextId() throws SQLException {
142 String query = "SELECT IFNULL(MAX(id),0) + 1 as next from card;";
143 Statement stmt = conn.createStatement();
144 ResultSet result = stmt.executeQuery(query);
145 result.next();
146 return result.getInt("next");
147 }
148
149 Account getAccountFromAccount(Account account) throws SQLException {
150 return getAccountFromCardNumber(account.getCardNumber());
151 }
152
153 Account getAccountFromCardNumber(String cardNumber) throws SQLException {
154 String query = "SELECT id, pin, balance\n" +
155 "FROM card\n" +
156 "WHERE number = ?;";
157
158 PreparedStatement pstmt = conn.prepareStatement(query);
159 pstmt.setString(1, cardNumber);
160
161 ResultSet rs = pstmt.executeQuery();
162 if (rs.next()) {
163 int id = rs.getInt("id");
164 String pin = rs.getString("pin");
165 int balance = rs.getInt("balance");
166 return new Account(id, cardNumber, pin, balance);
167 }
168
169 return null;
170 }
171
172 void deleteAccount(Account account) throws SQLException {
173 String query = "DELETE FROM CARD WHERE id = ?;";
174 PreparedStatement pstm = conn.prepareStatement(query);
175 pstm.setInt(1, account.getId());
176
177 int result = pstm.executeUpdate();
178 }
179
180 void updateBalance(Account account, int amount) throws SQLException {
181 String query = "UPDATE CARD SET balance = balance + ? where id = ?";
182 PreparedStatement pstmt = conn.prepareStatement(query);
183 pstmt.setInt(1, amount);
184 pstmt.setInt(2, account.getId());
185
186 int result = pstmt.executeUpdate();
187 }
188
189 Account createAccount(String cardNumber, String pin) throws SQLException {
190 int nextId = getNextId();
191 String query = "INSERT INTO card(id, number, pin)\n" +
192 "VALUES(?, ?, ?);";
193
194 PreparedStatement pstmt = conn.prepareStatement(query);
195 pstmt.setInt(1, nextId);
196 pstmt.setString(2, cardNumber);
197 pstmt.setString(3, pin);
198
199 boolean result = pstmt.execute();
200 if (!result) {
201 return getAccountFromCardNumber(cardNumber);
202 }
203 return null;
204 }
205}
206enum AccountOptions {
207 BALANCE("Balance"),
208 ADD_INCOME("Add income"),
209 TRANSFER("Do transfer"),
210 CLOSE("Close account"),
211 LOG_OUT("Log out");
212
213 private final String action;
214
215 AccountOptions(String action) {
216 this.action = action;
217 }
218
219 @Override
220 public String toString() {
221 return action;
222 }
223}
224
225abstract class Menu {
226 protected Scanner scanner;
227
228 protected abstract MainState start() throws SQLException;
229
230 protected <E extends Enum<E>> E getInput(List<E> options) {
231 Integer selection = -1;
232
233 while (selection == null || selection > options.size() || selection < 0) {
234 int menuChoice = 1;
235 for (E option : options) {
236 System.out.println(menuChoice++ + ". " + option);
237 }
238 System.out.println("0. Exit");
239 selection = getNumberFromInput();
240 }
241 System.out.println();
242 if (selection == 0) {
243 return null;
244 }
245 return options.get(selection - 1);
246 }
247
248 protected Integer getNumberFromInput() {
249 Integer selection = null;
250 try {
251 selection = scanner.nextInt();
252 } catch (NoSuchElementException e) {
253 System.out.println("Invalid input");
254 }
255 scanner.nextLine();
256 return selection;
257 }
258
259 protected String getNumberFromInputAsString() {
260 String selection = null;
261 try {
262 selection = scanner.next("\\d+");
263 } catch (NoSuchElementException e) {
264 System.out.println("Invalid input");
265 }
266 scanner.nextLine();
267 return selection;
268 }
269}
270
271class AccountMenu extends Menu {
272 private final List<AccountOptions> options;
273 private Account account;
274 private final AccountConnection db;
275
276 public AccountMenu(Scanner scanner, AccountConnection db) {
277 this.scanner = scanner;
278 this.db = db;
279
280 options = new ArrayList<>();
281 options.add(AccountOptions.BALANCE);
282 options.add(AccountOptions.ADD_INCOME);
283 options.add(AccountOptions.TRANSFER);
284 options.add(AccountOptions.CLOSE);
285 options.add(AccountOptions.LOG_OUT);
286 }
287
288 private Account login() throws SQLException {
289 account = null;
290 String cardNumber = null;
291 String pin = null;
292
293 while (cardNumber == null) {
294 System.out.println("Enter your card number:");
295 cardNumber = getNumberFromInputAsString();
296 }
297
298 while (pin == null) {
299 System.out.println("Enter your PIN:");
300 pin = getNumberFromInputAsString();
301 }
302
303 System.out.println();
304 Account account = db.getAccountFromCardNumber(cardNumber);
305 if (account == null || !account.verifyPin(pin)) {
306 return null;
307 }
308 return account;
309
310 }
311
312 public void createAccount() throws SQLException {
313 Account account;
314 String accountNumber = db.getNextAccountNumber();
315 String cardNumber = Account.getCardNumber(accountNumber);
316 String pin = Account.generatePin();
317
318 if (cardNumber != null) {
319 account = db.createAccount(cardNumber, pin);
320 } else {
321 System.out.println("Error generating number and pin");
322 return;
323 }
324 if (account == null) {
325 System.out.println("Error creating new account");
326 return;
327 }
328 System.out.println(
329 "Your card has been created\n" +
330 "Your card number\n" +
331 cardNumber + "\n" +
332 "Your card pin:\n" +
333 pin);
334 System.out.println();
335 }
336
337 private Account addIncome() throws SQLException {
338 System.out.println("How much would you like to deposit?");
339 Integer amount = getNumberFromInput();
340 System.out.println();
341
342 db.updateBalance(account, amount);
343 return db.getAccountFromAccount(account);
344
345 }
346
347 private void transfer() throws SQLException {
348 System.out.println("Which card would you like to transfer to?");
349 String transferToCardNumber = getNumberFromInputAsString();
350 Account transferTo = db.getAccountFromCardNumber(transferToCardNumber);
351
352 if (account.getCardNumber().equals(transferToCardNumber)) {
353 System.out.println("You can't transfer money to the same account!");
354 } else if (!Account.verifyCardNumber(transferToCardNumber)) {
355 System.out.println("Probably you made mistake in card number. Please try again!");
356 } else if (transferTo == null) {
357 System.out.println("Such a card does not exist.");
358 } else {
359 System.out.println("How much would you like to transfer?");
360 Integer amount = getNumberFromInput();
361 System.out.println();
362 if (amount != null) {
363 db.updateBalance(transferTo, amount);
364 db.updateBalance(account, -amount);
365
366 // refresh changes from DB
367 account = db.getAccountFromAccount(account);
368 }
369 }
370 System.out.println();
371 }
372
373 @Override
374 public MainState start() throws SQLException {
375 this.account = login();
376 if (account == null) {
377 System.out.println("Wrong card number or PIN!");
378 System.out.println();
379 return MainState.MAIN;
380 }
381
382 System.out.println("You have successfully logged in!");
383 System.out.println();
384
385 while (true) {
386 AccountOptions selection = getInput(options);
387 if (selection == null) {
388 return null;
389 }
390
391 switch (selection) {
392 case BALANCE:
393 System.out.println("Balance: " + account.getBalance());
394 break;
395 case ADD_INCOME:
396 account = addIncome();
397 break;
398 case TRANSFER:
399 transfer();
400 break;
401 case CLOSE:
402 db.deleteAccount(account);
403 // CONTINUES IN LOGOUT CASE
404 case LOG_OUT:
405 this.account = null;
406 return MainState.MAIN;
407 default:
408 return null;
409 }
410 System.out.println();
411 }
412 }
413}
414
415enum MainOptions {
416 CREATE_ACCOUNT("Create account"),
417 LOG_IN("Log into account");
418
419 private final String action;
420
421 MainOptions(String action) {
422 this.action = action;
423 }
424
425 @Override
426 public String toString() {
427 return action;
428 }
429}
430
431class MainMenu extends Menu {
432 private final List<MainOptions> options;
433
434 public MainMenu(Scanner scanner) {
435 this.scanner = scanner;
436 options = new ArrayList<>();
437
438 options.add(MainOptions.CREATE_ACCOUNT);
439 options.add(MainOptions.LOG_IN);
440 }
441
442 @Override
443 public MainState start() {
444 MainOptions selection = getInput(options);
445 if (selection == null) {
446 return null;
447 }
448
449 switch (selection) {
450 case CREATE_ACCOUNT:
451 return MainState.CREATE_ACCOUNT;
452 case LOG_IN:
453 return MainState.ACCOUNT;
454 default:
455 return null;
456 }
457
458 }
459}
460
461public class Main {
462 public static void main(String[] args) throws SQLException {
463 String dbFilePath = null;
464
465 for (int i = 0; i < args.length; i++) {
466 String currentArg = args[i];
467
468 if (currentArg.equals("-fileName") && args.length - 1 > i) {
469 dbFilePath = args[++i];
470 }
471 }
472
473 if (dbFilePath == null) {
474 System.out.println("Missing input file -fileName parameter");
475 return;
476 }
477
478 // Start program
479 MainState state = MainState.MAIN;
480 AccountConnection db = new AccountConnection(dbFilePath);
481 Scanner scanner = new Scanner(System.in);
482 MainMenu mainMenu = new MainMenu(scanner);
483 AccountMenu accountMenu = new AccountMenu(scanner, db);
484
485 try {
486 while (state != null) {
487 switch (state) {
488 case MAIN:
489 state = mainMenu.start();
490 break;
491 case ACCOUNT:
492 state = accountMenu.start();
493 break;
494 case CREATE_ACCOUNT:
495 accountMenu.createAccount();
496 state = MainState.MAIN;
497 break;
498 default:
499 state = null;
500 break;
501 }
502 }
503 } catch (SQLException throwables) {
504 throwables.printStackTrace();
505 } finally {
506 db.close();
507 }
508 }
509}
510
511