|
Bank |
|
1 // joi/9/bank/Bank.java 2 // 3 // 4 // Copyright 2003 Bill Campbell and Ethan Bolker 5 6 import java.util.*; 7 import java.io.*; 8 9 /** 10 * A Bank object simulates the behavior of a simple bank/ATM. 11 * It contains a Terminal object and a collection of 12 * BankAccount objects. 13 * 14 * The visit method opens this Bank for business, 15 * prompting the customer for input. 16 * 17 * It is persistent: it can save its state to a file and read it 18 * back at a later time. 19 * 20 * To create a Bank and open it for business issue the command 21 * <code>java Bank</code> with appropriate arguments. 22 * 23 * @see BankAccount 24 * @version 9 25 */ 26 27 public class Bank 28 implements Serializable 29 { 30 private String bankName; // the name of this Bank 31 private transient Terminal atm; // for communication with world 32 private int balance = 0; // total cash on hand 33 private int transactionCount = 0; // number of Bank transactions 34 private Month month; // the current month. 35 private Map accountList; // mapping names to accounts. 36 37 private int checkFee = 2; // cost for each check 38 private int transactionFee = 1; // fee for each transaction 39 private int monthlyCharge = 5; // monthly charge 40 private double interestRate = 0.05; // annual rate paid on savings 41 private int maxFreeTransactions = 3; // for savings accounts 42 43 // what the banker can ask of the bank 44 45 private static final String BANKER_COMMANDS = 46 "Banker commands: " + 47 "exit, open, customer, nextmonth, report, help."; 48 49 // what the customer can ask of the bank 50 51 private static final String CUSTOMER_TRANSACTIONS = 52 " Customer transactions: deposit, withdraw, transfer,\n" + 53 " balance, cash check, quit, help."; 54 55 /** 56 * Construct a Bank with the given name. 57 * 58 * @param bankName the name for this Bank. 59 */ 60 61 public Bank( String bankName ) 62 { 63 this.atm = atm; 64 this.bankName = bankName; 65 accountList = new TreeMap(); 66 month = new Month(); 67 } 68 69 /** 70 * Simulates interaction with a Bank. 71 * Presents the user with an interactive loop, prompting for 72 * banker transactions and in the case of the banker 73 * transaction "customer", an account id and further 74 * customer transactions. 75 */ 76 77 public void visit() 78 { 79 instructUser(); 80 81 String command; 82 while (!(command = 83 atm.readWord("banker command: ")).equals("exit")) { 84 85 if (command.startsWith("h")) { 86 help( BANKER_COMMANDS ); 87 } 88 else if (command.startsWith("o")) { 89 openNewAccount(); 90 } 91 else if (command.startsWith("n")) { 92 newMonth(); 93 } 94 else if (command.startsWith("r")) { 95 report(); 96 } 97 else if (command.startsWith( "c" ) ) { 98 BankAccount acct = whichAccount(); 99 if ( acct != null ) { 100 processTransactionsForAccount( acct ); 101 } 102 } 103 else { 104 // Unrecognized Request 105 atm.println( "unknown command: " + command ); 106 } 107 } 108 report(); 109 atm.println( "Goodbye from " + bankName ); 110 } 111 112 113 // Open a new bank account, 114 // prompting the user for information. 115 116 private void openNewAccount() 117 { 118 String accountName = atm.readWord( "Account name: " ); 119 char accountType = 120 atm.readChar( "Type of account (r/c/f/s): " ); 121 try { 122 int startup = readPosAmt( "Initial deposit: " ); 123 BankAccount newAccount; 124 switch( accountType ) { 125 case 'c': 126 newAccount = new CheckingAccount( startup, this ); 127 break; 128 case 'f': 129 newAccount = new FeeAccount( startup, this ); 130 break; 131 case 's': 132 newAccount = new SavingsAccount( startup, this ); 133 break; 134 case 'r': 135 newAccount = new RegularAccount( startup, this ); 136 break; 137 default: 138 atm.println("invalid account type: " + accountType); 139 return; 140 } 141 accountList.put( accountName, newAccount ); 142 atm.println( "opened new account " + accountName 143 + " with $" + startup ); 144 } 145 catch (NegativeAmountException e) { 146 atm.errPrintln( 147 "You cannot open an account with a negative balance"); 148 } 149 catch (InsufficientFundsException e) { 150 atm.errPrintln( "Initial deposit doesn't cover fee" ); 151 } 152 } 153 154 // Prompt the customer for transaction to process. 155 // Then send an appropriate message to the account. 156 157 private void processTransactionsForAccount( BankAccount acct ) 158 { 159 help( CUSTOMER_TRANSACTIONS ); 160 161 String transaction; 162 while (!(transaction = 163 atm.readWord(" transaction: ")).equals("quit")) { 164 165 try { 166 if ( transaction.startsWith( "h" ) ) { 167 help( CUSTOMER_TRANSACTIONS ); 168 } 169 else if ( transaction.startsWith( "d" ) ) { 170 int amount = readPosAmt( " amount: " ); 171 atm.println(" deposited " 172 + acct.deposit( amount )); 173 } 174 else if ( transaction.startsWith( "w" ) ) { 175 int amount = readPosAmt( " amount: " ); 176 atm.println(" withdrew " 177 + acct.withdraw( amount )); 178 } 179 else if ( transaction.startsWith( "c" ) ) { 180 int amount = readPosAmt( " amount of check: " ); 181 try { // to cast acct to CheckingAccount ... 182 atm.println(" cashed check for " + 183 ((CheckingAccount) acct).honorCheck( amount )); 184 } 185 catch (ClassCastException e) { 186 // if not a checking account, report error 187 atm.errPrintln( 188 " Sorry, not a checking account." ); 189 } 190 } 191 else if (transaction.startsWith("t")) { 192 atm.print( " to "); 193 BankAccount toacct = whichAccount(); 194 if (toacct != null) { 195 int amount = readPosAmt(" amount to transfer: "); 196 atm.println(" transfered " 197 + toacct.deposit(acct.withdraw(amount))); 198 } 199 } 200 else if (transaction.startsWith("b")) { 201 atm.println(" current balance " 202 + acct.requestBalance()); 203 } 204 else { 205 atm.println(" sorry, unknown transaction" ); 206 } 207 } 208 catch (InsufficientFundsException e) { 209 atm.errPrintln( " Insufficient funds " + 210 e.getMessage() ); 211 } 212 catch (NegativeAmountException e) { 213 atm.errPrintln(" Sorry, negative amounts disallowed." ); 214 } 215 atm.println(); 216 } 217 } 218 219 // Prompt for an account name (or number), look it up 220 // in the account list. If it's there, return it; 221 // otherwise report an error and return null. 222 223 private BankAccount whichAccount() 224 { 225 String accountName = atm.readWord( "account name: " ); 226 BankAccount account = (BankAccount) accountList.get(accountName); 227 if (account == null) { 228 atm.println( "not a valid account" ); 229 } 230 return account; 231 } 232 233 // Action to take when a new month starts. 234 // Update the month field by sending a next message. 235 // Loop on all accounts, sending each a newMonth message. 236 237 private void newMonth() 238 { 239 month.next(); 240 Iterator i = accountList.keySet().iterator(); 241 while ( i.hasNext() ) { 242 String name = (String) i.next(); 243 BankAccount acct = (BankAccount) accountList.get( name ); 244 try { 245 acct.newMonth(); 246 } 247 catch (InsufficientFundsException exception) { 248 atm.errPrintln( "Insufficient funds in account \"" + 249 name + "\" for monthly fee" ); 250 } 251 } 252 } 253 254 // Report bank activity. 255 // For each BankAccount, print the customer id (name or number), 256 // account balance and the number of transactions. 257 // Then print Bank totals. 258 259 private void report() 260 { 261 atm.println( bankName + " report for " + month ); 262 atm.println( "\nSummaries of individual accounts:" ); 263 atm.println( "account balance transaction count" ); 264 for (Iterator i = accountList.keySet().iterator(); 265 i.hasNext(); ) { 266 String accountName = (String) i.next(); 267 BankAccount acct = (BankAccount) accountList.get(accountName); 268 atm.println(accountName + "\t$" + acct.getBalance() + "\t\t" 269 + acct.getTransactionCount()); 270 } 271 atm.println( "\nBank totals"); 272 atm.println( "open accounts: " + getNumberOfAccounts() ); 273 atm.println( "cash on hand: $" + getBalance()); 274 atm.println( "transactions: " + getTransactionCount()); 275 atm.println(); 276 } 277 278 279 // Welcome the user to the bank and instruct her on 280 // her options. 281 282 private void instructUser() 283 { 284 atm.println( "Welcome to " + bankName ); 285 atm.println( month.toString() ); 286 atm.println( "Open some accounts and work with them." ); 287 help( BANKER_COMMANDS ); 288 } 289 290 // Display a help string. 291 292 private void help( String helpString ) 293 { 294 atm.println( helpString ); 295 atm.println(); 296 } 297 298 // Read amount prompted for from the atm. 299 // Throw a NegativeAmountException if amount < 0 300 301 private int readPosAmt( String prompt ) 302 throws NegativeAmountException 303 { 304 int amount = atm.readInt( prompt ); 305 if (amount < 0) { 306 throw new NegativeAmountException(); 307 } 308 return amount; 309 } 310 311 /** 312 * Increment bank balance by given amount. 313 * 314 * @param amount the amount increment. 315 */ 316 317 public void incrementBalance(int amount) 318 { 319 balance += amount; 320 } 321 322 /** 323 * Increment by one the count of transactions, 324 * for this bank. 325 */ 326 327 public void countTransaction() 328 { 329 transactionCount++; 330 } 331 332 /** 333 * Get the number of transactions performed by this bank. 334 * 335 * @return number of transactions performed. 336 */ 337 338 public int getTransactionCount( ) 339 { 340 return transactionCount ; 341 } 342 343 /** 344 * The charge this bank levies for cashing a check. 345 * 346 * @return check fee 347 */ 348 349 public int getCheckFee( ) 350 { 351 return checkFee ; 352 } 353 354 /** 355 * The charge this bank levies for a transaction. 356 * 357 * @return the transaction fee 358 */ 359 360 public int getTransactionFee( ) 361 { 362 return transactionFee ; 363 } 364 365 /** 366 * The charge this bank levies each month. 367 * 368 * @return the monthly charge 369 */ 370 371 public int getMonthlyCharge( ) 372 { 373 return monthlyCharge; 374 } 375 376 /** 377 * The current interest rate on savings. 378 * 379 * @return the interest rate 380 */ 381 382 public double getInterestRate( ) 383 { 384 return interestRate; 385 } 386 387 /** 388 * The number of free transactions per month. 389 * 390 * @return the number of transactions 391 */ 392 393 public int getMaxFreeTransactions() 394 { 395 return maxFreeTransactions; 396 } 397 398 /** 399 * Get the current bank balance. 400 * 401 * @return current bank balance. 402 */ 403 404 public int getBalance() 405 { 406 return balance; 407 } 408 409 /** 410 * Get the current number of open accounts. 411 * 412 * @return number of open accounts. 413 */ 414 415 public int getNumberOfAccounts() 416 { 417 return accountList.size(); 418 } 419 420 /** 421 * Set the atm for this Bank. 422 * 423 * @param atm the Bank's atm. 424 */ 425 426 public void setAtm( Terminal atm ) { 427 this.atm = atm; 428 } 429 430 431 /** 432 * Run the simulation by creating and then visiting a new Bank. 433 * <p> 434 * A -e argument causes the input to be echoed. 435 * This can be useful for executing the program against 436 * a test script, e.g., 437 * <pre> 438 * java Bank -e < Bank.in 439 * </pre> 440 * 441 * The -f argument reads the bank's state from the specified 442 * file, and writes it to that file when the program exits. 443 * 444 * @param args the command line arguments: 445 * <pre> 446 * -e echo input. 447 * -f filename 448 * bankName any other command line argument. 449 * </pre> 450 */ 451 452 public static void main( String[] args ) 453 { 454 boolean echo = false; 455 String bankFileName = null; 456 String bankName = "Persistent Bank"; 457 Bank theBank = null; 458 459 // parse the command line arguments 460 for (int i = 0; i < args.length; i++ ) { 461 if (args[i].equals("-e")) { // echo input to output 462 echo = true; 463 continue; 464 } 465 if (args[i].equals("-f")) { // read/write Bank from/to file 466 bankFileName = args[++i]; 467 continue; 468 } 469 } 470 471 // create a new Bank or read one from a file 472 if (bankFileName == null) { 473 theBank = new Bank( bankName ); 474 } 475 else { 476 theBank = readBank( bankName, bankFileName ); 477 } 478 479 // give the Bank a Terminal, then visit 480 theBank.setAtm(new Terminal(echo)); 481 theBank.visit(); 482 483 // write theBank's state to a file if required 484 if (bankFileName != null) { 485 writeBank(theBank, bankFileName); 486 } 487 } 488 489 // Read a Bank from a file (create it if file doesn't exist). 490 // 491 // @param bankName the name of the Bank 492 // @param bankFileName the name of the file containing the Bank 493 // 494 // @return the Bank 495 496 private static Bank readBank(String bankName, String bankFileName) 497 { 498 File file = new File( bankFileName ); 499 if (!file.exists()) { 500 return new Bank( bankName ); 501 } 502 ObjectInputStream inStream = null; 503 try { 504 inStream = new ObjectInputStream( 505 new FileInputStream( file ) ); 506 Bank bank = (Bank)inStream.readObject(); 507 System.out.println( 508 "Bank state read from file " + bankFileName); 509 return bank; 510 } 511 catch (Exception e ) { 512 System.err.println( 513 "Problem reading " + bankFileName ); 514 System.err.println(e); 515 System.exit(1); 516 } 517 finally { 518 try { 519 inStream.close(); 520 } 521 catch (Exception e) { 522 } 523 } 524 return null; // you can never get here 525 } 526 527 528 // Write a Bank to a file. 529 // 530 // @param bank the Bank 531 // @param fileName the name of the file to write the Bank to 532 533 private static void writeBank( Bank bank, String fileName) 534 { 535 ObjectOutputStream outStream = null; 536 try { 537 outStream = new ObjectOutputStream( 538 new FileOutputStream( fileName ) ); 539 outStream.writeObject( bank ); 540 System.out.println( 541 "Bank state written to file " + fileName); 542 } 543 catch (Exception e ) { 544 System.err.println( 545 "Problem writing " + fileName ); 546 } 547 finally { 548 try { 549 outStream.close(); 550 } 551 catch (Exception e ) { 552 } 553 } 554 } 555 } 556
|
Bank |
|