diff --git a/SplitwiseMachineCoding/src/com/splitwise/machine/coding/CollectionUtils.java b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/CollectionUtils.java new file mode 100644 index 0000000..1e942cf --- /dev/null +++ b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/CollectionUtils.java @@ -0,0 +1,15 @@ +package com.splitwise.machine.coding; + +import java.util.Collection; + +public class CollectionUtils { + + public E get(Collection collection, E object) { + for (E e : collection) { + if (e.hashCode() == object.hashCode() && e.equals(object)) { + return e; + } + } + return null; + } +} diff --git a/SplitwiseMachineCoding/src/com/splitwise/machine/coding/Driver.java b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/Driver.java new file mode 100644 index 0000000..7ed419f --- /dev/null +++ b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/Driver.java @@ -0,0 +1,64 @@ +package com.splitwise.machine.coding; + +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + +public class Driver { + + static LedgerService ledgerService = new LedgerService(); + + public static void main(String[] args) { + + Scanner sc = new Scanner(System.in); + + while (sc.hasNext()) { + String line = sc.nextLine(); + String[] inputs = line.split(" "); + String command = inputs[0]; + if (command.equals("EXPENSE")) { + String payerId = inputs[1]; + double amount = Double.valueOf(inputs[2]); + int users = Integer.valueOf(inputs[3]); + List splitTo = new ArrayList<>(users); + int x = 0; + for (; x < users; x++) { + splitTo.add(inputs[x + 4]); + } + String expenseType = inputs[x + 4]; + List scales = new ArrayList<>(); + if (!expenseType.equals("EQUAL")) { + for (int y = 0; y < users; y++) { + scales.add(Integer.valueOf((inputs[x + 4 + 1 + y]))); + } + } + + createUserBase(); + ledgerService.splitExpenses(expenseType, payerId, amount, splitTo, scales); + + } + if (command.equals("SHOW")) { + inputs = line.split(" "); + if (inputs.length > 1) { + ledgerService.getUserExpensesSheet(inputs[1]); + } else { + ledgerService.getAllUserExppenses(); + } + } + } + } + + private static void createUserBase() { + // Creating dummy users for now , it can be created using some file or + // some other ways. + User user1 = new User("u1", "User1", "User1@gmail.com", "123"); + User user2 = new User("u2", "User2", "User2@gmail.com", "1234"); + User user3 = new User("u3", "User3", "User3@gmail.com", "1235"); + User user4 = new User("u4", "User4", "User4@gmail.com", "123455"); + UserBase.addUser(user1); + UserBase.addUser(user2); + UserBase.addUser(user3); + UserBase.addUser(user4); + + } +} diff --git a/SplitwiseMachineCoding/src/com/splitwise/machine/coding/EqualExpense.java b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/EqualExpense.java new file mode 100644 index 0000000..f375744 --- /dev/null +++ b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/EqualExpense.java @@ -0,0 +1,23 @@ +package com.splitwise.machine.coding; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; + +public class EqualExpense implements Expense { + + @Override + public List splitExpense(double amount, List splitTo, List scales) { + List userExpenseProfiles = new ArrayList<>(); + + DecimalFormat df = new DecimalFormat("#.##"); + double equalAmount = Double.valueOf(df.format(amount/splitTo.size())); + + for(int x=0; x< splitTo.size(); x++) { + UserExpenseProfile profile = new UserExpenseProfile(splitTo.get(x), equalAmount); + userExpenseProfiles.add(profile); + } + return userExpenseProfiles; + } + +} diff --git a/SplitwiseMachineCoding/src/com/splitwise/machine/coding/ExactExpense.java b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/ExactExpense.java new file mode 100644 index 0000000..3254cb9 --- /dev/null +++ b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/ExactExpense.java @@ -0,0 +1,18 @@ +package com.splitwise.machine.coding; + +import java.util.ArrayList; +import java.util.List; + +public class ExactExpense implements Expense { + + @Override + public List splitExpense(double amount, List splitTo, List scales) { + List userExpenseProfiles = new ArrayList<>(); + + for(int x=0; x< splitTo.size(); x++) { + UserExpenseProfile profile = new UserExpenseProfile(splitTo.get(x), scales.get(x)); + userExpenseProfiles.add(profile); + } + return userExpenseProfiles; + } +} diff --git a/SplitwiseMachineCoding/src/com/splitwise/machine/coding/Expense.java b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/Expense.java new file mode 100644 index 0000000..904c1af --- /dev/null +++ b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/Expense.java @@ -0,0 +1,8 @@ +package com.splitwise.machine.coding; + +import java.util.List; + +public interface Expense { + + List splitExpense(double amount, List splitTo, List scales); +} diff --git a/SplitwiseMachineCoding/src/com/splitwise/machine/coding/ExpenseFactory.java b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/ExpenseFactory.java new file mode 100644 index 0000000..45dbe64 --- /dev/null +++ b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/ExpenseFactory.java @@ -0,0 +1,24 @@ +package com.splitwise.machine.coding; + +public class ExpenseFactory { + + public static Expense getInstance(ExpenseType expenseType) { + + switch (expenseType) { + case EQUAL: + return new EqualExpense(); + case EXACT: + return new ExactExpense(); + case PERCENT: + return new PercentExpense(); + + default: + break; + } + return null; + } +} + +enum ExpenseType { + EQUAL,EXACT,PERCENT +} \ No newline at end of file diff --git a/SplitwiseMachineCoding/src/com/splitwise/machine/coding/LedgerBook.java b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/LedgerBook.java new file mode 100644 index 0000000..9ae5977 --- /dev/null +++ b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/LedgerBook.java @@ -0,0 +1,82 @@ +package com.splitwise.machine.coding; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * @author Hakim.s + * + */ +public class LedgerBook { + + private Map> userExpensesProfile; + + public LedgerBook() { + + this.userExpensesProfile = new HashMap<>(); + } + + public void addExpenseEntry(String payer, UserExpenseProfile paidTo) { + + UserExpenseProfile payerProfile = new UserExpenseProfile(payer, (paidTo.getExpenses() * -1)); + + updateExpense(payer, paidTo); + + // update reverse association. + updateExpense(paidTo.getUserId(), payerProfile); + + } + + private void updateExpense(String payer, UserExpenseProfile paidTo) { + + if (getUserExpensesProfile().containsKey(payer)) { + List profiles = getUserExpensesProfile().get(payer); + + if (profiles.contains(paidTo)) { + UserExpenseProfile existingProfile = new CollectionUtils().get(profiles, paidTo); + existingProfile.setExpenses(existingProfile.getExpenses() + paidTo.getExpenses()); + // System.out.println(existingProfile.getExpenses()); + } else { + profiles.add(paidTo); + } + + } else { + // Add new entry. + List userExpenseProfiles = new ArrayList<>(); + userExpenseProfiles.add(paidTo); + getUserExpensesProfile().put(payer, userExpenseProfiles); + } + } + + public List getExpensesForUser(String userId) { + return getUserExpensesProfile().get(userId); + } + + public Map> getAllLedgerExpenses() { + + Map> filteredUserExpenses = new HashMap<>(); + Set>> entrySet = userExpensesProfile.entrySet(); + for (Entry> entry : entrySet) { + List value = entry.getValue(); + List filteredProfiles = new ArrayList<>(); + for (UserExpenseProfile userExpenseProfile : value) { + if (userExpenseProfile.getExpenses() < 0) { + filteredProfiles.add(userExpenseProfile); + } + } + if(!filteredProfiles.isEmpty()) + filteredUserExpenses.put(entry.getKey(), filteredProfiles); + } + return filteredUserExpenses; + + } + + private Map> getUserExpensesProfile() { + return userExpensesProfile; + } + +} diff --git a/SplitwiseMachineCoding/src/com/splitwise/machine/coding/LedgerService.java b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/LedgerService.java new file mode 100644 index 0000000..ab104e5 --- /dev/null +++ b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/LedgerService.java @@ -0,0 +1,37 @@ +package com.splitwise.machine.coding; + +import java.util.List; +import java.util.Map; + +public class LedgerService { + + LedgerBook ledgerBook = new LedgerBook(); + + public void splitExpenses(String expenseType, String payer, double amount, List splitTo, + List scales) { + List userExpenseProfiles = null; + + // Calling factory which will create a Expense Type object, we can take + // any type in future. + Expense expenseTypeInstance = ExpenseFactory.getInstance(ExpenseType.valueOf(expenseType)); + userExpenseProfiles = expenseTypeInstance.splitExpense(amount, splitTo, scales); + + for (UserExpenseProfile userExpenseProfile : userExpenseProfiles) { + if (!payer.equals(userExpenseProfile.getUserId())) { + ledgerBook.addExpenseEntry(payer, userExpenseProfile); + } + } + + } + + public void getUserExpensesSheet(String userId) { + List userExpensesSheet = ledgerBook.getExpensesForUser(userId); + PrintReport.printUserBalances(userId, userExpensesSheet); + } + + public void getAllUserExppenses() { + Map> userExpensesSheet = ledgerBook.getAllLedgerExpenses(); + + PrintReport.printAllBalances(userExpensesSheet); + } +} diff --git a/SplitwiseMachineCoding/src/com/splitwise/machine/coding/PercentExpense.java b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/PercentExpense.java new file mode 100644 index 0000000..6318711 --- /dev/null +++ b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/PercentExpense.java @@ -0,0 +1,23 @@ +package com.splitwise.machine.coding; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; + +public class PercentExpense implements Expense { + + @Override + public List splitExpense(double amount, List splitTo, List scales) { + List userExpenseProfiles = new ArrayList<>(); + + for (int x = 0; x < splitTo.size(); x++) { + DecimalFormat df = new DecimalFormat("#.##"); + double percentAmount = Double.valueOf(df.format((amount * scales.get(x)) / 100)); + + UserExpenseProfile profile = new UserExpenseProfile(splitTo.get(x), percentAmount); + userExpenseProfiles.add(profile); + } + return userExpenseProfiles; + } + +} diff --git a/SplitwiseMachineCoding/src/com/splitwise/machine/coding/PrintReport.java b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/PrintReport.java new file mode 100644 index 0000000..5d9dff9 --- /dev/null +++ b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/PrintReport.java @@ -0,0 +1,52 @@ +package com.splitwise.machine.coding; + +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * @author Hakim.s + * + * Optional requirement 3 can be done using this class. A way to show + * the passbook for a user. [The entries should show all the + * transactions a user was part of. You can print in any format you + * like] + * + */ +public class PrintReport { + + public static void printUserBalances(String userId, List userExpensesSheet) { + if (userExpensesSheet == null || userExpensesSheet.isEmpty()) { + System.out.println("No balances"); + return; + } + for (UserExpenseProfile userExpenseProfile : userExpensesSheet) { + if (userExpenseProfile.getExpenses() < 0) { + printMessage(userId, userExpenseProfile.getUserId(), userExpenseProfile.getExpenses()); + } else if (userExpenseProfile.getExpenses() > 0) { + printMessage(userExpenseProfile.getUserId(), userId, userExpenseProfile.getExpenses()); + + } + } + + } + + public static void printAllBalances(Map> userExpensesMap) { + if (userExpensesMap == null || userExpensesMap.isEmpty()) { + System.out.println("No balances"); + return; + } + Set>> entries = userExpensesMap.entrySet(); + for (Entry> entry : entries) { + printUserBalances(entry.getKey(), entry.getValue()); + } + + } + + private static void printMessage(String borrower, String payer, double expenses) { + System.out.println(UserBase.getUser(borrower).getName() + " owes " + UserBase.getUser(payer).getName() + ": " + + Math.abs(expenses)); + } + +} diff --git a/SplitwiseMachineCoding/src/com/splitwise/machine/coding/User.java b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/User.java new file mode 100644 index 0000000..bfd2021 --- /dev/null +++ b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/User.java @@ -0,0 +1,52 @@ +package com.splitwise.machine.coding; + +public class User { + + String userId; + + String name; + + String emailId; + + String mobileNumber; + + public User(String userId, String name, String emailId, String mobileNumber) { + this.userId = userId; + this.name = name; + this.emailId = emailId; + this.mobileNumber = mobileNumber; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEmailId() { + return emailId; + } + + public void setEmailId(String emailId) { + this.emailId = emailId; + } + + public String getMobileNumber() { + return mobileNumber; + } + + public void setMobileNumber(String mobileNumber) { + this.mobileNumber = mobileNumber; + } + +} diff --git a/SplitwiseMachineCoding/src/com/splitwise/machine/coding/UserBase.java b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/UserBase.java new file mode 100644 index 0000000..1d106df --- /dev/null +++ b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/UserBase.java @@ -0,0 +1,17 @@ +package com.splitwise.machine.coding; + +import java.util.HashMap; +import java.util.Map; + +public class UserBase { + + static Map userMap = new HashMap(); + + public static User getUser(String userId) { + return userMap.get(userId); + } + + public static void addUser(User user) { + userMap.put(user.getUserId(), user); + } +} diff --git a/SplitwiseMachineCoding/src/com/splitwise/machine/coding/UserExpenseProfile.java b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/UserExpenseProfile.java new file mode 100644 index 0000000..a6696e0 --- /dev/null +++ b/SplitwiseMachineCoding/src/com/splitwise/machine/coding/UserExpenseProfile.java @@ -0,0 +1,55 @@ +package com.splitwise.machine.coding; + +public class UserExpenseProfile { + + private String userId; + + private double expenses; + + public UserExpenseProfile(String userId, double expenses) { + this.userId = userId; + this.expenses = expenses; + } + + public double getExpenses() { + return expenses; + } + + public void setExpenses(double expenses) { + this.expenses = expenses; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((userId == null) ? 0 : userId.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + UserExpenseProfile other = (UserExpenseProfile) obj; + if (userId == null) { + if (other.userId != null) + return false; + } else if (!userId.equals(other.userId)) + return false; + return true; + } + +}