/* *** READ ME FIRST *** @Name: midterm.dll @Comment: Midterm Project @Date: 2006.06.17 @Author: Vincenzo Maggio 1. Prerequisites 2. Explanations 3. Warnings 1. Prerequisites Some data are saved on disk in a CSV format !!! Foresee a Data Validation: in a string NEVER accept a comma ',' 2. Explanations 1. There are 3 files on disk: - primRate.dat = Fed. Res. Prime Rate - custindx.ndx = Customers Index (..not yet) - customer.dat = Customers Data (..not yet) 2. Prime Rate contains only a single value, that's the Federal Reserve prime rate. All the bank rates are dinamycally calculated at run-time, as delta from this value 3. Index File Structure - When filled, it's a list of int64 values, CR/LF separated. Each of these values is associated with a customer. 4a. Account' Types - Field is an int, of which only the first 6 bits are used. Expandable. Values are read as binary values, in bits and it is per account: Loan Saving Checking Gold Student Standard L S C G U A So for example: - a standard checking (AC) in binary is 001001 = 9 dec. - a US is 010010 = 18 - a GC is 001100 = 12 and so on 4b. THE LEAST SIGNIFICATIVE 3 BITS MUST BE: 001 OR 010 OR 100 !! SIMPLY CALL AN INSTANCE OF THE CLASS CBMAccount FOR EVERY ACCOUNT OWNED BY A CUSTOMER. 4c. CUSTOMERS WITH MORE THAN 1 ACCOUNT DON'T NEED TO BE MIXED IN HERE !! 3. >>> W A R N I N G <<< *** BE AWARE OF THE DAWG *** ;p Known issues or bugs: - this code is most likely UNSAFE or unpredictable behavior if MORE than 1 instance is run !! ive 2 think bout dat + needs to implement some file lock, or even better a mechanism inside the class CBankManagement, to handle a 'record' lock, for example running any of the class' modifiers methods on a single-thread mutex or sumtin... keeping track in a bi-dimensional array of both thread PID AND on which customer it's operating, that's the customer' index. So if there is a request on a Customer AND there is a thread PID associated with that number, the request must wait until PID ends (predict an associated timeout timer too...) or sumtin... + another idea could be to keep track of WHO is logged on ALL the bank's employees - from cashier to admins board and president - give different rights.. a collection of users, login, passwords, date, privileges, etc + further dev instead of a single instance of the class CBankManagement an idea could be to allocate an array, to manage multiple banks or branches... - LIMIT: the whole database is in RAM how many customers instances may bear ? how many bank accounts ? to our keen reader we leave the guess */ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.IO; using System.Text; using NUnit.Framework; namespace bankMgmt { public class CBankManagement { private static CBMPrimeRate primeRate; private static CBMAccountsRates ratesTable; private static CBMCustomersIndex custIndex; private static CBMCustomerData custData; private static CBMAccount account; private static CBMTransactions transaction; // private static double dPrimeRate; private static long lCustNo; private static bool bDB_up_running; // is the system up and running ? // // constructor public CBankManagement() { // load prime rate primeRate = new CBMPrimeRate(); // load accounts' rates ratesTable = new CBMAccountsRates(); // load the customers' index custIndex = new CBMCustomersIndex(); // load the customers from the disk // LoadCustomers(); // load the accounts account = new CBMAccount(); // load the transactions transaction = new CBMTransactions(); bDB_up_running = true; } // Modifiers Methods // // this method loads the index file from the disk private void LoadCustomers() { const string strFileNdx = "custindx.ndx"; // const string strFileDat = "customer.dat"; // if file exists if (System.IO.File.Exists(strFileNdx)) { using (System.IO.StreamReader src = System.IO.File.OpenText(strFileNdx)) { string s; while (!src.EndOfStream) { s = src.ReadLine(); lCustNo = System.Convert.ToInt64(s, 10); // custIndex.Add( lCustNo, null, null); } src.Close(); } } else // create it { // if the file does NOT exist, create it with a zero size using (System.IO.FileStream dest = System.IO.File.Create(strFileNdx, 0)) { dest.Flush(); dest.Close(); } } } public void AddAccount(long accNo, long owner, long auth0, long auth1, long auth2, double balance, int type, int rate) { if (!custIndex.ContainsCust(owner)) return; if (!ratesTable.ContainsType(type)) return; if (auth0 != null) if ( !custIndex.ContainsCust(auth0)) return; if (auth1 != null) if ( !custIndex.ContainsCust(auth1)) return; if (auth2 != null) if ( !custIndex.ContainsCust(auth2)) return; account.Add(accNo, owner, auth0, auth1, auth2, balance, type, rate); } public void AddTransaction(long key, double dAmount, string description) { if (!custIndex.ContainsCust(key)) { System.Console.WriteLine("\nError: Account number: {0} does not exist." + "\nVoid Transaction.\n"); return; } CBMAccountData cd = account.Value(key); if (dAmount < 0 && cd.CLEAN) transaction.Add(key, dAmount, description); // inform user that transaction's been entered } } // class end internal class CBMCustName { private string nameLast; private string nameFirst; public CBMCustName() { } public CBMCustName(string slast, string sfirst) { nameLast = slast; nameFirst = sfirst; } public string LastName() { return nameLast; } public override string ToString() { return nameLast + ", " + nameFirst; } } internal class CBMCustomerData { private long lCustNo; // customer number, owner, authorized by default // on the owned accounts private string strNameLast; private string strNameFirst; private string strNameMid; private string strAddress1; private string strAddress2; private string strCity; private string strState; private string strZIP; private string strPhoneOffice; private string strPhoneHome; private string strPhoneCell; private string strFaxOffice; private string strEmail; } public class CBMCustomersIndex { private static Dictionary custIndex; private static CBMCustName custName; public int Count { get { return custIndex.Count; } } // Constructor public CBMCustomersIndex() { custIndex = new Dictionary(); } // Accessor Methods public Queue LookUpCustNo(string slast) { Queue qResult = new Queue(); foreach (KeyValuePair kvp in custIndex) { CBMCustName cn = kvp.Value; if (slast == cn.LastName().Substring(0, slast.Length)) { qResult.Enqueue(kvp.Key); } } return qResult; } public bool ContainsCust(long cust) { if (custIndex.ContainsKey(cust)) return true; else { System.Console.WriteLine("Error: Customer no. {0} does not exist.", cust); return false; } } public string CustName(long cNo) { try { return custIndex[cNo].ToString(); } catch { System.Console.WriteLine("Account No. {0} does not exist.", cNo); } return null; } // Modifiers Methods public void AddCust(long custNo, string slast, string sfirst) { custName = new CBMCustName(slast, sfirst); try { custIndex.Add(custNo, custName); } catch { Console.WriteLine("ERROR: Duplicate key\n" + "Account # {0} already exists.\n" + "Customer NOT added. Review the Account' number.", custNo); } } } public class CBMPrimeRate { private double dPrimeRate; // constructor public CBMPrimeRate() { const string strFileName = "primrate.dat"; // if file exists if (System.IO.File.Exists(strFileName)) { using (System.IO.StreamReader src = System.IO.File.OpenText(strFileName)) { string s = src.ReadLine(); dPrimeRate = System.Convert.ToDouble(s); src.Close(); } } else // create it { // if the file does NOT exist, create it and // write in it a primerate fixed value of 5.7500% // that later can be changed using (System.IO.FileStream dest = System.IO.File.Create(strFileName, 10)) { byte[] info = new System.Text.UTF8Encoding(true).GetBytes("5.7500"); dest.Write(info, 0, info.Length); dest.Flush(); dest.Close(); } } } // get public double Rate() { const string strPrimeRateFileName = "primrate.dat"; // if file exists and it should... using (System.IO.StreamReader src = System.IO.File.OpenText(strPrimeRateFileName)) { string s = src.ReadLine(); src.Close(); return System.Convert.ToDouble(s); } } // set public void SetRate(double newValue, CBMAccountsRates ratesTable) { // changes the value in ram dPrimeRate = newValue; // changes the value on disk const string strFileName = "primrate.dat"; string strInfo = System.Convert.ToString(newValue); Byte[] cNewRate = new UTF8Encoding(true).GetBytes(strInfo); // remember to catch eventual exception... System.IO.File.WriteAllBytes(strFileName, cNewRate); ratesTable.Refresh(); } } public class CBMAccountsRates { // bitmask // Loan Saving Checking Gold Student Standard // L S C G U A // IMPORTANT !! // record on disk is : , in CSV format !! // is a bitmask for Account Type // is the delta from the prime rate private static SortedList accRates; private static Dictionary accType; private static Dictionary accDelta; private static Dictionary minIntrest; // constructor public CBMAccountsRates() { accRates = new SortedList(); accType = new Dictionary(); accDelta = new Dictionary(); minIntrest = new Dictionary(); /* const string strFileName = "rates.dat"; if (System.IO.File.Exists(strFileName)) { using (System.IO.StreamReader src = System.IO.File.OpenText(strFileName)) { string s; while ( (s = src.ReadLine()) != null ); { // int charNo = s.IndexOf('\u002c'); string[] sub = s.Split(','); int index = System.Convert.ToInt32(sub[0]); double dPrimeRate = System.Convert.ToDouble(sub[1]); accRates.Add(index, dPrimeRate); // catch the exception for heavens sake } src.Close(); } } else { using (System.IO.FileStream dest = System.IO.File.Create(strFileName, 16)) { byte[] info = new System.Text.UTF8Encoding(true).GetBytes("5.7500"); dest.Write(info, 0, info.Length); dest.Flush(); dest.Close(); dest.writeline(); } } */ // inasmuch the previous code is disabled // THE NEXT STUB OF CODE IS TEMPORARY // just to init the KeyValuePair<> // // init accounts types accType.Add(9, "Standard Checking".PadRight(20, ' ')); accType.Add(10, "Student Checking".PadRight(20, ' ')); accType.Add(12, "Gold Checking".PadRight(20, ' ')); accType.Add(17, "Standard Saving".PadRight(20, ' ')); accType.Add(18, "Student Saving".PadRight(20, ' ')); accType.Add(20, "Gold Saving".PadRight(20, ' ')); accType.Add(33, "Standard Loan".PadRight(20, ' ')); accType.Add(34, "Student Loan".PadRight(20, ' ')); accType.Add(36, "Gold Loan".PadRight(20, ' ')); // accRates.Add(9, 0.0d); // CA accRates.Add(10, 0.0d); // CU accRates.Add(12, 0.0d); // CG accRates.Add(17, 0.0d); // SA accRates.Add(18, 0.0d); // SU accRates.Add(20, 0.0d); // SG accRates.Add(33, 0.0d); // LA accRates.Add(34, 0.0d); // LU accRates.Add(36, 0.0d); // LG // minIntrest.Add(9, 0.0d); // CA minIntrest.Add(10, 0.0d); // CU minIntrest.Add(12, 0.90d); // CG minIntrest.Add(17, 1.0d); // SA minIntrest.Add(18, 1.0d); // SU minIntrest.Add(20, 2.0d); // SG minIntrest.Add(33, 1.750d); // LA minIntrest.Add(34, 1.250d); // LU minIntrest.Add(36, 1.0d); // LG // accDelta.Add(9, 0.0d); // CA accDelta.Add(10, 0.0d); // CU accDelta.Add(12, 24.0d); // CG accDelta.Add(17, 15.0d); // SA accDelta.Add(18, 28.0d); // SU accDelta.Add(20, 11.0d); // SG accDelta.Add(33, 16.0d); // LA accDelta.Add(34, 15.0d); // LU accDelta.Add(36, 18.0d); // LG Refresh(); } // accessor methods public double AccRate(int ndx) { if (accRates.ContainsKey(ndx)) return accRates[ndx]; System.Console.WriteLine("ERROR: Account type #{0} does not exist", ndx); return 0.0d; } public double AccDelta(int ndx) { if (accRates.ContainsKey(ndx)) return accDelta[ndx]; System.Console.WriteLine("ERROR: Account type #{0} does not exist", ndx); return 0.0d; } public double AccMin(int ndx) { if (accRates.ContainsKey(ndx)) return minIntrest[ndx]; System.Console.WriteLine("ERROR: Account type #{0} does not exist", ndx); return 0.0d; } public override string ToString() { string sResult = " ACCOUNTS\n Code\tName\t\t\tValue\n\n"; foreach (KeyValuePair kvp in accRates) { sResult += (kvp.Key.ToString() + "\t" + accType[kvp.Key].ToString() + "\t" + kvp.Value.ToString() + "\n"); } return sResult; } public string ToStringDelta() { string sResult = " ACCOUNTS INTERESTS DELTA\n Code\tName\t\t\tDelta\n\n"; foreach (KeyValuePair kvp in accDelta) { sResult += (kvp.Key.ToString() + "\t" + accType[kvp.Key].ToString() + "\t" + @"PR/" + accDelta[kvp.Key].ToString() + "\n"); } return sResult; } public string ToStringMin() { string sResult = " ACCOUNTS MINIMAL INTERESTS\n Code\tName\t\t\tMin.Int.\n\n"; foreach (KeyValuePair kvp in minIntrest) { sResult += (kvp.Key.ToString() + "\t" + accType[kvp.Key].ToString() + "\t" + minIntrest[kvp.Key].ToString() + "\n"); } return sResult; } public bool ContainsType(int type) { if (accType.ContainsKey(type)) return true; else { System.Console.WriteLine("Error: an Account of type: {0} does not exist.", type); return false; } } public int Count() { return accRates.Count; } // modifier methods public void Refresh() { CBMPrimeRate diskRateValue = new CBMPrimeRate(); double dPrimeRate = diskRateValue.Rate(); foreach (KeyValuePair kvp in accType) { if (accDelta[kvp.Key] != 0) accRates[kvp.Key] = minIntrest[kvp.Key] + (dPrimeRate / accDelta[kvp.Key]); } } // minimal rate change public void SetMin(int iIndex, double dNewRate) { if (minIntrest.ContainsKey(iIndex)) { minIntrest[iIndex] = dNewRate; Refresh(); } else { System.Console.WriteLine("ERROR: Account type: {0} does not exist." + @"\nNothing's been updated.", iIndex); } } // delta rate change public void SetDelta(int iIndex, double dNewRate) { if (accDelta.ContainsKey(iIndex)) { accDelta[iIndex] = dNewRate; Refresh(); } else { System.Console.WriteLine("ERROR: Account type: {0} does not exist." + "\nNothing's been updated.", iIndex); } } } public class CBMAccountData { private static long lAccNo; private static long lOwner; // authorized by default private static long lAuth0; // who is authorized private static long lAuth1; // who is authorized private static long lAuth2; // who is authorized private static long lMaxNeg; private static double dBalance; private static int iAccType; private static int iAccRating; public bool CLEAN { get { return CLEAN; } set { } } public CBMAccountData(long accNo, long owner, long auth0, long auth1, long auth2, double balance, int type, int rate) { lAccNo = accNo; lOwner = owner; lAuth0 = auth0; lAuth1 = auth1; lAuth2 = auth2; dBalance = balance; iAccType = type; iAccRating = rate; CLEAN = true; lMaxNeg = 0L; } } public class CBMAccount { private static Dictionary account; private CBMAccountData accData; public CBMAccount() { account = new Dictionary(); } public CBMAccountData Value(long key) { if (account.ContainsKey(key)) { return account[key]; } return null; } public void Add(long accNo, long owner, long auth0, long auth1, long auth2, double balance, int type, int rate) { accData = new CBMAccountData(accNo, owner, auth0, auth1, auth2, balance, type, rate); account.Add(accNo, accData); } } public class CBMTransactionData { private long lAccount; private double dAmount; private DateTime daDate; private string description; // constructor public CBMTransactionData( long accNo, double total, string desc) { lAccount = accNo; dAmount = total; daDate = new DateTime(); description = desc; } } public class CBMTransactions { private static Dictionary dictTrans; private CBMTransactionData td; public CBMTransactions() { dictTrans = new Dictionary(); } // accessor methods // modifer methods public void Add( long accNo, double total, string description) { td = new CBMTransactionData( accNo, total, description); if ( td != null) dictTrans.Add(accNo, td); } } // _______________________________________________________________________ [TestFixture] public class Tests { CBMPrimeRate pr = new CBMPrimeRate(); CBMAccountsRates rates = new CBMAccountsRates(); CBMCustomersIndex custNdx = new CBMCustomersIndex(); [Test] public void T_01_PrimeRate_File_IsOnDisk() { System.Console.WriteLine("1. Serie of Tests - PRIME RATE AND RATES" + "\n========================================\n"); double dPrimeRate; const string strFileName = "primrate.dat"; System.Console.WriteLine("Test 1.a:\n\n..trying to read PrimeRate from file"); // if file exists if (System.IO.File.Exists(strFileName)) { using (System.IO.StreamReader src = System.IO.File.OpenText(strFileName)) { string s = src.ReadLine(); dPrimeRate = System.Convert.ToDouble(s); src.Close(); System.Console.WriteLine("a. Prime Rate file exists on disk - Passed"); } } else { System.Console.WriteLine("a. Prime Rate file does not exist on disk yet -- Passed"); dPrimeRate = 5.75d; } Assert.AreEqual(dPrimeRate, pr.Rate()); System.Console.WriteLine("b. Today prime rate value is: {0}% -- Passed", pr.Rate()); System.Console.WriteLine("c. Actual values of Interests are:\n" + rates.ToString()); } [Test] public void T_02_PrimeRate_Change() { pr.SetRate(6.25d, rates); System.Console.WriteLine("Test 1.b:\n\na. Prime rate value chabged to: {0}%\n", pr.Rate()); Assert.AreEqual(6.25d, pr.Rate()); System.Console.WriteLine(" Actual values of Interests are:\n" + rates.ToString()); } [Test] public void T_03_PrimeRate_Change() { pr.SetRate(6.0d, rates); System.Console.WriteLine("Test 1.c:\n\nPrime rate value chabged to: {0}%\n", pr.Rate()); Assert.AreEqual(6.0d, pr.Rate()); System.Console.WriteLine(" Actual values of Interests are:\n" + rates.ToString()); } [Test] public void T_04_Print_Min_Intrest() { System.Console.WriteLine("2. Serie of Tests - ACCOUNT TYPES\n" + "=================================\n"); System.Console.WriteLine("Test 2.a\n"); Assert.AreEqual(9, rates.Count()); System.Console.WriteLine("{0}", rates.ToStringMin()); System.Console.WriteLine("Assert: there are 9 accounts types = passed\n"); } [Test] public void T_05_Print_Delta_Int() { System.Console.WriteLine("Test 2.b\n"); Assert.AreEqual(9, rates.Count()); System.Console.WriteLine("{0}", rates.ToStringDelta()); System.Console.WriteLine("Assert: there are 9 accounts types = passed\n"); } [Test] public void T_06_Rising_Ex() { System.Console.WriteLine("Test 2.c - Account Type' Exception\n"); for (int i = 1; i < 4; i++) { System.Console.WriteLine("..trying to get the rate for account Type {0}," + " which does not exist", 100 - i); System.Console.WriteLine("Default value {0}.0d - Assert: previous " + "error caught !\n", rates.AccRate(100 - i)); } System.Console.WriteLine("Test 2.d - Delta Exceptions\n"); System.Console.WriteLine("..trying to get the delta for account Type" + " 44, which does not exist"); System.Console.WriteLine("{0}", rates.AccDelta(44)); System.Console.WriteLine("Assert: not existing Account exception caught!\n"); System.Console.WriteLine("..trying to set the delta for account Type" + " 55, which does not exist"); rates.SetDelta(55, 1.75); System.Console.WriteLine("Assert: not existing Account exception caught!\n"); } [Test] public void T_07_Add_Some_Accounts() { System.Console.WriteLine("3. Serie of Tests - CUSTOMERS ACCOUNTS\n" + "======================================\n"); System.Console.WriteLine("Test 3.a:\n\n..trying to create customers accounts"); custNdx.AddCust(412458741, "Cannavaro", "Fabio"); custNdx.AddCust(412523659, "Buffon", "Gianluigi"); custNdx.AddCust(412623214, "Materazzi", "Marco"); custNdx.AddCust(413236593, "Grosso", "Fabio"); custNdx.AddCust(231642529, "Thornell", "Richard"); custNdx.AddCust(321854122, "Baxter", "Steve"); custNdx.AddCust(321724128, "Connor", "Hugh"); custNdx.AddCust(231657128, "Fry", "Rich"); custNdx.AddCust(413114582, "Doe", "John"); custNdx.AddCust(413114583, "Doe", "Jane"); Assert.AreEqual(10, custNdx.Count); System.Console.WriteLine("Created 10 new accounts -- Passed\n"); } [Test] public void T_08_Account_Ex_2() { System.Console.WriteLine("Test 3.b\n"); System.Console.WriteLine("..trying to insert a new account at an already existing key"); custNdx.AddCust(412523659, "Doe", "Jane"); System.Console.WriteLine("Assert: duplicate keys exception caught !\n"); System.Console.WriteLine("..trying to insert a new account at an already existing key"); custNdx.AddCust(321854122, "Doe", "John"); System.Console.WriteLine("Assert: duplicate keys exception caught !\n"); } [Test] public void T_09_More_Ex() { System.Console.WriteLine("Test 3.c\n"); long key = 412458741L; System.Console.WriteLine("..trying to get Customer' name through the existing key 412458741L"); System.Console.WriteLine("Account #{0} is registered to: {1}", key, custNdx.CustName(key)); key = 112233445L; System.Console.WriteLine("\n..trying to get Customer' name through a NOT existing key 112233445L"); System.Console.WriteLine("{0}\t{1}", key, custNdx.CustName(key)); System.Console.WriteLine("Assert: not existing key exception caught !\n"); } // } // class Tests } // namespace