diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..b1c0b882 --- /dev/null +++ b/.gitignore @@ -0,0 +1,145 @@ +# Created by https://www.toptal.com/developers/gitignore/api/windows,java,intellij+all +# Edit at https://www.toptal.com/developers/gitignore?templates=windows,java,intellij+all + +### Intellij+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf +.gitignore + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij+all Patch ### +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. + +.idea/* + +!.idea/codeStyles +!.idea/runConfigurations + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/windows,java,intellij+all diff --git a/lib/lombok-1.18.26.jar b/lib/lombok-1.18.26.jar new file mode 100644 index 00000000..8f95540f Binary files /dev/null and b/lib/lombok-1.18.26.jar differ diff --git a/me.smartstore/src/Main.java b/me.smartstore/src/Main.java new file mode 100644 index 00000000..bc6c3ac1 --- /dev/null +++ b/me.smartstore/src/Main.java @@ -0,0 +1,8 @@ +public class Main { + public static void main(String[] args) { + + //SmartStoreApp.getInstance()의 객체가 리턴되어야 test()있는 것, 없는 것 동일하게 사용이 가능 + SmartStoreApp.getInstance().test().run(); //function chaining +// SmartStoreApp.getInstance().run(); + } +} diff --git a/me.smartstore/src/Service/CustomerService.java b/me.smartstore/src/Service/CustomerService.java new file mode 100644 index 00000000..583394a2 --- /dev/null +++ b/me.smartstore/src/Service/CustomerService.java @@ -0,0 +1,389 @@ +package Service; + +import domain.customer.Customer; +import domain.customer.Customers; +import domain.group.Group; +import domain.group.GroupType; +import domain.group.Groups; +import domain.menu.CustomerMenu; +import handler.exception.*; + +import java.util.NoSuchElementException; +import java.util.function.BiPredicate; +import java.util.function.Predicate; + +import static resources.Message.*; + +public class CustomerService { + + private static final Customers customers = Customers.getInstance(); + private static final CustomerMenu customerMenu = CustomerMenu.getInstance(); + private static final Groups groups = Groups.getInstance(); + + private static CustomerService customerService; + + public static CustomerService getInstance() { + if (customerService == null) { + customerService = new CustomerService(); + } + return customerService; + } + + private CustomerService() { + } + + public void addCustomer() { + // 고객 등록을 하기 전에 group size가 1이라면 먼저 등록을 유도 + // Groups의 사이즈가 1이라면 default로 등록한 NONE 그룹 외에은 등록되지 않았다는 의미 + if (groups.size() == 1) { + System.out.println(ERR_MSG_INVALID_GROUP_EMPTY); + return; + } + + while (true) { + try { + System.out.println("How many customer to input?"); + int repeatNum = customerMenu.nextInt(END_MSG); + addCustomerDetail(repeatNum); + } catch (InputEndException e) { + System.out.println(e.getMessage()); + break; + } catch (InputFormatException | NoSuchElementException | IllegalStateException e) { + System.out.println(e.getMessage()); + } + } + } + + private void addCustomerDetail(int repeatNum) { + + for (int count = 0; count < repeatNum; count++) { + try { + System.out.println("====== Customer " + (count + 1) + " Info. ======\n"); + System.out.println("After input all the items, select 'Back' to complete the registration."); + + // 신규 Customer 객체 생성 후 개인별로 GroupType을 판별하여 설정해 준다. + Customer regCustomer = getCustomerDetail(); + regCustomer.setGroupType(findGroupType(regCustomer)); + + // 고객등록이 완료되면 Customers 배열 객체에 add 한다. + customers.add(regCustomer); + System.out.println("[Registration Complete] => " + regCustomer); + + } catch (InputEndException e) { + //end 입력시 입력 루틴을 종료 + System.out.println(e.getMessage()); + break; + } catch (InputRangeException | InputFormatException e) { + System.out.println(e.getMessage()); + } catch (NoSuchGroupException e) { + System.out.println(e.getMessage()); + break; + } + } + } + + private Customer getCustomerDetail() { + if (groups.size() == 1) { + throw new NoSuchGroupException(); + } + String cusName = ""; + String cusId = ""; + Integer cusTotalTime = null; + Integer cusTotalPay = null; + + while (true) { + try { + int choice = showCustomerDetailMenu(); + + if (choice == 1) cusName = getCustomerName(); + if (choice == 2) cusId = getCustomerId(); + if (choice == 3) cusTotalTime = getCustomerTotalTime(); + if (choice == 4) cusTotalPay = getCustomerTotalPay(); + if (choice == 5) { + //입력 종료시 name과 id가 입력되지 않았다면 재입력 유도 + if (cusName.isEmpty() || cusId.isEmpty() || cusTotalTime == null || + cusTotalPay == null) { + System.out.println("Customer Name : " + cusName + ", CustomerId : " + cusId + + ", CustomerTotalTime : " + cusTotalTime + ", CustomerTotalPay : " + cusTotalPay); + throw new InputEmptyException(); + } + break; + } + } catch (InputRangeException | NumberFormatException | InputFormatException e) { + System.out.println(e.getMessage()); + } catch (InputEmptyException e) { + System.out.println("[ERROR] => " + e.getMessage()); + } //end of try-catch + } //end of while + return new Customer(cusName, cusId, cusTotalTime, cusTotalPay); + } + + private int showCustomerDetailMenu() { + return customerMenu.selectMenu(new String[]{ + "Customer Name", + "Customer ID", + "Customer Spent Time", + "Customer Total Pay", + "Back" + }); + } + + private String getCustomerName() { + while (true) { + try { + System.out.println("Input Customer's Name:"); + String name = customerMenu.nextLine(END_MSG); + if (name.isEmpty()) throw new InputEmptyException(); + return name; + } catch (InputEndException e) { + System.out.println(e.getMessage()); + break; + } + } + throw new InputEmptyException(); + } + + private String getCustomerId() { + while (true) { + try { + System.out.println("Input Customer's ID:"); + String inputId = customerMenu.nextLine(END_MSG); + + if (inputId.isEmpty()) throw new InputEmptyException(); + + // customerId는 중복이 있을 수 없으므로 중복 체크 후 값을 반환 + // true : id 중복 / false : id 중복 아님 + if (isDupeCusId.test(inputId)) { + // 중복이면 다시 입력 유도 + throw new InvalidIdException(); + } + return inputId; + } catch (InputEndException e) { + System.out.println(e.getMessage()); + break; + } catch (InvalidIdException e) { + System.out.println(e.getMessage()); + } + } + throw new InputEmptyException(); + } + + private Integer getCustomerTotalTime() { + while (true) { + try { + System.out.println("Input Customer's Spent Time:"); + return customerMenu.nextInt(END_MSG); + } catch (InputFormatException e) { + System.out.println(e.getMessage()); + } catch (InputEndException e) { + System.out.println(e.getMessage()); + break; + } + } + throw new InputEmptyException(); + } + + private Integer getCustomerTotalPay() { + while (true) { + try { + System.out.println("Input Customer's Total Payment:"); + return customerMenu.nextInt(END_MSG); + } catch (InputFormatException e) { + System.out.println(e.getMessage()); + } catch (InputEndException e) { + System.out.println(e.getMessage()); + break; + } + } + throw new InputEmptyException(); + } + + public void showCustomerAll() { + if (customers.size() == 0) { + System.out.println(ERR_MSG_INVALID_ARR_EMPTY); + return; + } + + System.out.println("======= Customer Info. ======="); + for (int i = 0; i < customers.size(); i++) { + System.out.print("No. " + (i + 1) + " => "); + System.out.println(customers.get(i)); + } + } + + public void updateCustomer() { + if (customers.size() == 0) { + System.out.println(ERR_MSG_INVALID_ARR_EMPTY); + return; + } + + while (true) { + try { + //1. 먼저 등록된 고객 목록을 모두 보여주고 index 번호를 입력받는다. + showCustomerAll(); + int input = getCusIndex(); + + //2. 입력받은 index가 정상이라면 세부 설정 항목으로 이동 + //index는 0부터 시작하므로 입력받은 수에서 1을 빼준다. + setCustomerDetail(customers.get(input - 1)); + } catch (IndexOutOfBoundsException | InputFormatException | InputRangeException e) { + System.out.println(e.getMessage()); + } catch (InputEndException e) { + System.out.println(e.getMessage()); + break; + } + } + } + + private int getCusIndex() throws InputEndException, InputFormatException { + System.out.print("Which customer ( 1 ~ " + customers.size() + " )? "); + int choice = customerMenu.nextInt(END_MSG); + if (customerMenu.isInvalidRange(choice, customers.size())) { + throw new InputRangeException(); + } + return choice; + } + + private void setCustomerDetail(Customer targetCustomer) { + + while (true) { + try { + int choice = showCustomerDetailMenu(); + + if (choice == 1) targetCustomer.setCusName(getCustomerName()); + if (choice == 2) targetCustomer.setCusId(getCustomerId()); + if (choice == 3) targetCustomer.setCusTotalTime(getCustomerTotalTime()); + if (choice == 4) targetCustomer.setCusTotalPay(getCustomerTotalPay()); + if (choice == 5) { + if (targetCustomer.getCusName().isEmpty() || targetCustomer.getCusId().isEmpty()) { + throw new InputEmptyException(); + } else break; + } + } catch (InputRangeException | InputFormatException | InputEmptyException e) { + System.out.println(e.getMessage()); + } catch (InputEndException e) { + System.out.println(e.getMessage()); + break; + } + //end of try-catch + }//end of while + } + + public void deleteCustomer() { + + if (customers.size() == 0) { + System.out.println(ERR_MSG_INVALID_ARR_EMPTY); + return; + } + showCustomerAll(); + + while (true) { + try { + System.out.print("Which customer ( 1 ~ " + customers.size() + " )? "); + int input = customerMenu.nextInt(END_MSG); + + //true - 범위를 벗어남 / false - 범위 내 + if (customerMenu.isInvalidRange(input, customers.size())) { + throw new InputRangeException(); + } else { + System.out.println(customers.pop(input - 1)); + break; + } + } catch (InputEndException e) { + System.out.println(e.getMessage()); + break; + } catch (InputFormatException | InputRangeException e) { + System.out.println(e.getMessage()); + } + } + } + + /** + * Customer가 추가되거나 Group Parameter가 변경되면 고객 등급 초기화 + */ + public void refresh() { + if (groups.size() == 0 || customers.size() == 0) { + System.out.println(ERR_MSG_INVALID_ARR_EMPTY); + return; + } + try { + //customers[] 배열의 GroupType 갱신 + for (int i = 0; i < customers.size(); i++) { + customers.get(i).setGroupType(findGroupType(customers.get(i))); + } + } catch (ArrayEmptyException | IndexOutOfBoundsException | NoSuchGroupException e) { + System.out.println(e.getMessage()); + } + } + + /** + * 사용자의 이용시간과 금액을 등록된 기준별로 매칭하여 반환 + * + * @param customer : Customer 객체 + * @return GroupType + */ + private GroupType findGroupType(Customer customer) { + // groups size가 1이라면 등록을 먼저 하도록 유도 + if (groups.size() == 1) { + throw new NoSuchGroupException(); + } + // groups/get(1) => GENERAL Type + // Customer의 사용시간과 이용금액이 GENERAL Type 기준보다 작으면 NONE + if (lessThanGeneral.test(groups.get(1), customer)) { + return GroupType.NONE; + } + + // VVIP 부터 GENERAL까지 역순으로 Group Parameter 기준에 맞는지 체크 + for (int i = (groups.size() - 1); i > 0; i--) { + try { + if (compareParameter.test(groups.get(i), customer)) { + return groups.get(i).getGroupType(); + } + } catch (ArrayEmptyException | IndexOutOfBoundsException e) { + System.out.println(e.getMessage()); + break; + } + } + // for문을 다 돌았는데도 맞는 조건이 없다면 NONE + return GroupType.NONE; + } + + /** + * 입력받은 customerID의 중복을 체크 + * + * @param inputId : 중복 체크할 customerId + * @return : true : id 중복 / false : id 중복 아님 + */ + Predicate isDupeCusId = inputId -> { + for (int i = 0; i < customers.size(); i++) { + if (customers.get(i).getCusId().equals(inputId)) { + return true; + } + } + return false; + }; + + /** + * Customer의 총 이용시간과 총 이용금액이 GENERAL Type 기준보다 작은지 체크 + * 작으면 true / 그렇지 않으면 false + */ + BiPredicate lessThanGeneral = (group, customer) -> { + if (customer.getCusTotalPay() < group.getParameter().getMinPay() || + customer.getCusTotalTime() < group.getParameter().getMinTime()) { + return true; + } + return false; + }; + + /** + * Customer의 Parameter의 값이 Group에 등록된 Parameter 값보다 크거나 같으면 true + * 아니면 false return + */ + BiPredicate compareParameter = (group, customer) -> { + if (customer.getCusTotalPay() >= group.getParameter().getMinPay() && + customer.getCusTotalTime() >= group.getParameter().getMinTime()) { + return true; + } + return false; + }; +} diff --git a/me.smartstore/src/Service/GroupService.java b/me.smartstore/src/Service/GroupService.java new file mode 100644 index 00000000..2846ee1f --- /dev/null +++ b/me.smartstore/src/Service/GroupService.java @@ -0,0 +1,236 @@ +package Service; + +import domain.group.Group; +import domain.group.GroupType; +import domain.group.Groups; +import domain.group.Parameter; +import domain.menu.GroupMenu; +import handler.exception.*; + +import java.util.Arrays; + +import static resources.Message.END_MSG; + +public class GroupService { + + private static final Groups groups = Groups.getInstance(); + private static final CustomerService customerService = CustomerService.getInstance(); + private static final GroupMenu groupMenu = GroupMenu.getInstance(); + + private static GroupService groupService; + + public static GroupService getInstance() { + if (groupService == null) { + groupService = new GroupService(); + } + return groupService; + } + + private GroupService() { + } + + public void setParameter() { + //Parameter 신규 등록 + while (true) { + try { + GroupType groupType = selectGroup(); + // GroupType에 해당하는 group 객체를 찾아야 함 + System.out.println("Select Group : " + groupType); + + //Groups에서 입력한 group이 등록되어 있는지 확인 + Group findGroup = groups.findGroup(groupType); + //GroupType이 null이 아니라면 이미 초기화 되어있음 + if (findGroup != null) { + System.out.println("\n" + findGroup.getGroupType() + " group already exists."); + System.out.println("\n" + findGroup); + break; + } else { + setParameterDetail(groupType); + } + } catch (InputEmptyException | InputRangeException | InputFormatException e) { + System.out.println(e.getMessage()); + // groups에 해당 그룹이 등록되어 있지 않다면 등록 메뉴로 이동 + } catch (InputEndException | NoSuchGroupException e) { + System.out.println(e.getMessage()); + break; + } catch (NullPointerException e) { + throw new NoSuchGroupException(); + } + } + } + + private void setParameterDetail(GroupType groupType) { + // time, pay 사용자 입력을 받은 후 groups에 add한다. + Parameter parameter = getParameterValue(); + groups.add(new Group(parameter, groupType)); + + // 파라미터가 변경되었거나 추가되는 경우, 고객 등급 분류를 다시 해야 한다. + customerService.refresh(); + } + + + private GroupType selectGroup() { + try { + System.out.println("Which Group (GENERAL (G), VIP (V), VVIP (VV))?"); + String choice = groupMenu.nextLineUpper(END_MSG); + if (choice.isEmpty()) throw new InputEmptyException(); + + // 입력한 group명이 null이 아니라면 GroupType을 찾는다. + GroupType searchGroupType = searchByInput(choice); + + // 입력한 GroupType을 찾지 못했다면 잘못된 입력이므로 예외 발생 + if (searchGroupType == null) throw new InputRangeException(); + + // 아니라면 찾은 GroupType을 return + return searchGroupType; + } catch (NoSuchGroupException | IllegalStateException | NullPointerException e) { + throw new InputRangeException(); + } + } + + private Parameter getParameterValue() { //초기화 할 때만 호출 가능 + Integer minTime = null; + Integer minPay = null; + + while (true) { + try { + int choice = getParamMenuNum(); + + if (choice == 1) minTime = getParamMinTime(); + if (choice == 2) minPay = getParamMinPay(); + if (choice == 3) { + if (minTime != null && minPay != null) { + break; + } + throw new InputEmptyException(); + } + } catch (InputEmptyException | InputFormatException | InputEndException e) { + System.out.println(e.getMessage()); + } + } + return new Parameter(minTime, minPay); + } + + private void setParameterValue(Parameter parameter) { + while (true) { + try { + int choice = getParamMenuNum(); + + if (choice == 1) parameter.setMinTime(getParamMinTime()); + if (choice == 2) parameter.setMinPay(getParamMinPay()); + if (choice == 3) { + if (parameter.getMinTime() != null || parameter.getMinPay() != null) { + break; + } + throw new InputEmptyException(); + } + } catch (InputEmptyException | InputFormatException | InputRangeException e) { + System.out.println(e.getMessage()); + } + } + } + + private int getParamMenuNum() { + return groupMenu.selectMenu(new String[]{ + "Minimum Spent Time", + "Minimum Total Pay", + "Back" + }); + } + + private Integer getParamMinTime() { + while (true) { + try { + System.out.println("Input Minimum Spent Time"); + return groupMenu.nextInt(END_MSG); + } catch (InputFormatException e) { + System.out.println(e.getMessage()); + } catch (InputEndException e) { + System.out.println(e.getMessage()); + break; + } + } + throw new InputEmptyException(); + } + + private Integer getParamMinPay() throws InputEndException { + while (true) { + try { + System.out.println("Input Minimum Total Pay"); + return groupMenu.nextInt(END_MSG); + } catch (InputFormatException e) { + System.out.println(e.getMessage()); + } catch (InputEndException e) { + System.out.println(e.getMessage()); + break; + } + } + throw new InputEmptyException(); + } + + public GroupType searchByInput(String input) { + if (input.isEmpty()) throw new InputEmptyException(); + + // GroupType ENUM을 배열 형태로 변환 후 NONE 그룹은 제외하고 + // 사용자가 입력한 문자에 해당하는 GroupType을 찾아서 반환한다. + return Arrays.stream(GroupType.values()) + .filter(type -> !type.isName("NONE")) // GroupType NONE은 제외. + .filter(type -> type.isName(input)) + .findFirst() + .orElseThrow(NoSuchGroupException::new); + } + + public void updateParameter() { + while (true) { + try { + GroupType groupType = selectGroup(); + System.out.println("Select Group : " + groupType); + + Group target = groups.findGroup(groupType); + //Group 검색 결과가 null이면 존재하지 않는 Group이므로 등록을 유도 + if (target == null) throw new NoSuchGroupException(); + + Parameter parameter = target.getParameter(); + + //Parameter 검색 결과가 null이면 존재하지 않는 Group이므로 등록을 유도 + if (parameter == null) throw new NoSuchGroupException(); + + //Parameter가 존재한다면 세부 항목 입력 메서드로 이동 + setParameterValue(parameter); + } catch (InputEndException | NoSuchGroupException e) { + System.out.println((e.getMessage())); + break; + } catch (InputEmptyException | InputRangeException e) { + System.out.println(e.getMessage()); + } catch (NullPointerException e) { + throw new NoSuchGroupException(); + } + } + } + + public void showParameter() { + while (true) { + try { + GroupType groupType = selectGroup(); + System.out.println("Select Group : " + groupType); + + Group target = groups.findGroup(groupType); + + //찾은 그룹이 null이거나 예외가 발생했다면 없는 그룹이므로 등록을 유도 + if (target == null) throw new NoSuchGroupException(); + + //해당 그룹을 찾았다면 출력 + System.out.println("===================================="); + System.out.println("GroupType: " + groupType); + System.out.println(target.getParameter()); + System.out.println("===================================="); + } catch (InputEndException | NoSuchGroupException e) { + System.out.println(e.getMessage()); + break; + } catch (InputRangeException e) { + System.out.println(e.getMessage()); + } + } + } +} + diff --git a/me.smartstore/src/Service/SummaryService.java b/me.smartstore/src/Service/SummaryService.java new file mode 100644 index 00000000..22354639 --- /dev/null +++ b/me.smartstore/src/Service/SummaryService.java @@ -0,0 +1,134 @@ +package Service; + +import domain.customer.Customer; +import domain.customer.Customers; +import domain.group.Group; +import domain.group.Groups; +import handler.exception.ArrayEmptyException; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; + +import static resources.Message.ERR_MSG_GROUP_CUSTOMER_EMPTY; +import static resources.Message.ERR_MSG_INVALID_ARR_EMPTY; + +public class SummaryService { + + private static final Customers customers = Customers.getInstance(); + private static final Groups groups = Groups.getInstance(); + private static SummaryService summaryService; + + public static SummaryService getInstance() { + if (summaryService == null) { + summaryService = new SummaryService(); + } + return summaryService; + } + + private SummaryService() { + } + + private String showSummaryHeader(Group group) { + StringBuilder returnStr = new StringBuilder("\n==========================================="); + returnStr.append("\nGroup : ") + .append(group.getGroupType()) + .append(" ( Time : ") + .append(group.getParameter().getMinTime()) + .append(", Pay : ") + .append(group.getParameter().getMinPay()) + .append(" )") + .append("\n==========================================="); + return returnStr.toString(); + } + + public void showDefaultSummary() { + if (customers.size() == 0) { + System.out.println(ERR_MSG_INVALID_ARR_EMPTY); + return; + } + + Customer[] customerArr = customers.getCustomers(); + showSummary(customerArr); + } + + private void showSummary(Customer[] customerArr) { + try { + for (int i = 0; i < groups.size(); i++) { + System.out.println(showSummaryHeader(groups.get(i))); + Customer[] resultArr = arrayByGroupType(groups.get(i), customerArr); + + //그룹별로 분류한 배열의 사이즈가 0이면 해당 고객이 존재하지 않는 그룹이므로 넘기기 + if (resultArr.length == 0) { + System.out.println(ERR_MSG_GROUP_CUSTOMER_EMPTY); + continue; + } + + AtomicInteger index = new AtomicInteger(); + Arrays.stream(resultArr) + .map(customer -> "No.\t" + (index.getAndIncrement() + 1) + " => " + customer) + .forEach(System.out::println); + } + } catch (IndexOutOfBoundsException | ArrayEmptyException e) { + System.out.println(e.getMessage()); + return; + } + } + + /** + * 매개변수로 넘겨 받은 Customer[] 배열에서 GroupType에 일치하는 Customer 객체를 배열로 만들어서 return. + * + * @param group : 그룹 객체 + * @param customerArr : Customer[] 객체 배열 + * @return 매개변수로 넘겨 받은 GroupType과 일치하는 Custmer[] 객체 배열 + */ + private Customer[] arrayByGroupType(Group group, Customer[] customerArr) { + return Arrays.stream(customerArr) + .filter(Objects::nonNull) + .filter(customer -> group.isGroupType(customer.getGroupType())) + .toArray(Customer[]::new); + } + + /** + * Type별로 정렬하여 Summary 출력 + * TODO 코드 중복을 많이 줄였지만 이걸 더 추상화 할 수 없을까? + * + * @param sortOrder : ASC : false / DESC : true (정렬 option) + */ + public void showByName(boolean sortOrder) { + Comparator byName = Comparator.comparing(Customer::getCusName); + showCommonSorted(byName, sortOrder); + } + + public void showByTime(boolean sortOrder) { + Comparator byTime = Comparator.comparing(Customer::getCusTotalTime); + showCommonSorted(byTime, sortOrder); + } + + public void showByPayment(boolean sortOrder) { + Comparator byPayment = Comparator.comparing(Customer::getCusTotalPay); + showCommonSorted(byPayment, sortOrder); + } + + private void showCommonSorted(Comparator comparator, boolean sortOrder) { + Customer[] customerArr = arraySort(comparator, sortOrder); + showSummary(customerArr); + } + + /** + * comparator를 모듈화 하여 정렬 로직을 재사용. + * Stream을 적극 활용함으로서 Null safe한 코드로 재탄생 + * + * @param comparator : lambda expression + * @param sortOrder : false - ASC(default) / true - DESC + * @return Customer[] + */ + private Customer[] arraySort(Comparator comparator, boolean sortOrder) { + if (sortOrder) comparator = comparator.reversed(); + return Arrays.stream(customers.getCustomers()) + .filter(Objects::nonNull) //null 객체는 제외 + .sorted(comparator) + .toArray(Customer[]::new); + } +} diff --git a/me.smartstore/src/SmartStoreApp.java b/me.smartstore/src/SmartStoreApp.java new file mode 100644 index 00000000..512746c7 --- /dev/null +++ b/me.smartstore/src/SmartStoreApp.java @@ -0,0 +1,37 @@ +import domain.menu.MainMenu; +import test.TestCase; + +public class SmartStoreApp { + private final MainMenu mainMenu = MainMenu.getInstance(); + + private static SmartStoreApp smartStoreApp; + + public static SmartStoreApp getInstance() { + if (smartStoreApp == null) { + smartStoreApp = new SmartStoreApp(); + } + return smartStoreApp; + } + + private SmartStoreApp() {} + + public void details() { + System.out.println("\n==========================================="); + System.out.println(" Title : SmartStore Customer Classification"); + System.out.println(" Release Date : 23.05.10"); + System.out.println(" Revision by matrixpower1004@gmail.com"); + System.out.println("===========================================\n"); + } + + public SmartStoreApp test() { + TestCase testCase = new TestCase(); + testCase.buildTestCase(); + + return this; // smartStoreApp + } + + public void run() { + details(); + mainMenu.manage(); + } +} diff --git a/me.smartstore/src/domain/customer/Customer.java b/me.smartstore/src/domain/customer/Customer.java new file mode 100644 index 00000000..bba65de6 --- /dev/null +++ b/me.smartstore/src/domain/customer/Customer.java @@ -0,0 +1,90 @@ +package domain.customer; + +import domain.group.GroupType; + +import java.util.Objects; + +public class Customer { + private String cusName; + private String cusId; + private Integer cusTotalTime; + private Integer cusTotalPay; + + // Customer가 Group 객체 자체를 가지고 있을 필요가 있나? + // 필요한 것은 분류된 GroupType만 있으면 된다. + private GroupType groupType; + + public Customer() { + } + + public Customer(String cusName, String cusId, Integer cusTotalTime, Integer cusTotalPay) { + this.cusName = cusName; + this.cusId = cusId; + this.cusTotalTime = cusTotalTime; + this.cusTotalPay = cusTotalPay; + } + + public String getCusName() { + return cusName; + } + + public void setCusName(String cusName) { + this.cusName = cusName; + } + + public String getCusId() { + return cusId; + } + + public void setCusId(String cusId) { + this.cusId = cusId; + } + + public Integer getCusTotalTime() { + return cusTotalTime; + } + + public void setCusTotalTime(Integer cusTotalTime) { + this.cusTotalTime = cusTotalTime; + } + + public Integer getCusTotalPay() { + return cusTotalPay; + } + + public void setCusTotalPay(Integer cusTotalPay) { + this.cusTotalPay = cusTotalPay; + } + + public GroupType getGroupType() { + return groupType; + } + + public void setGroupType(GroupType groupType) { + this.groupType = groupType; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Customer customer = (Customer) o; + return Objects.equals(cusId, customer.cusId); + } + + @Override + public int hashCode() { + return Objects.hash(cusId); + } + + @Override + public String toString() { + return "Customer{" + + "cusName='" + cusName + '\'' + + ", cusId='" + cusId + '\'' + + ", cusTotalTime=" + cusTotalTime + + ", cusTotalPay=" + cusTotalPay + + ", groupType=" + groupType + + '}'; + } +} diff --git a/me.smartstore/src/domain/customer/Customers.java b/me.smartstore/src/domain/customer/Customers.java new file mode 100644 index 00000000..9e597462 --- /dev/null +++ b/me.smartstore/src/domain/customer/Customers.java @@ -0,0 +1,49 @@ +package domain.customer; + +import util.DArray; + +import java.util.Objects; +import java.util.stream.IntStream; + +/** + * 정적인 객체 배열을 동적 배열로 만들어주는 DArray를 상속 + * DArray 내부의 메소드들은 Collections 클래스에 이미 구현되어 있는 것이지만 + * 학습을 위하여 직접 구현해 보자. + */ +public class Customers extends DArray { + + private static Customers customers; + + public static Customers getInstance() { + if (customers == null) { + customers = new Customers(); + } + return customers; + } + + private Customers() { + } + +/** + * old logic + */ +// public Customer[] getCustomers() { +// Customer[] customerArr = new Customer[size]; +// for (int i = 0; i < size; i++) { +// customerArr[i] = customers.get(i); +// } +// return customerArr; +// } + + /** + * Stream을 이용하여 Customer[] 배열을 반환하도록 변경 + * for statement와 Stream 어떤 방법이 더 나을까? + * @return Customer[] 객체 배열 + */ + public Customer[] getCustomers() { + return IntStream.range(0, customers.size()) + .mapToObj(index -> customers.get(index)) + .filter(Objects::nonNull) + .toArray(Customer[]::new); + } +} diff --git a/me.smartstore/src/domain/group/Group.java b/me.smartstore/src/domain/group/Group.java new file mode 100644 index 00000000..878b0891 --- /dev/null +++ b/me.smartstore/src/domain/group/Group.java @@ -0,0 +1,61 @@ +package domain.group; + +import java.util.Objects; + +public class Group { + private Parameter parameter; // 분류기준 + private GroupType groupType; // 그룹 타입 + + public Group() { + } + + public Group(GroupType groupType) { + this.groupType = groupType; + } + + public Group(Parameter parameter, GroupType groupType) { + this.parameter = parameter; + this.groupType = groupType; + } + + public Parameter getParameter() { + return parameter; + } + + public void setParameter(Parameter parameter) { + this.parameter = parameter; + } + + public void setGroupType(GroupType groupType) { + this.groupType = groupType; + } + + public GroupType getGroupType() { + return groupType; + } + + public boolean isGroupType(GroupType groupType) { + return this.groupType == groupType; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Group group = (Group) o; + return Objects.equals(parameter, group.parameter) && groupType == group.groupType; + } + + @Override + public int hashCode() { + return Objects.hash(parameter, groupType); + } + + @Override + public String toString() { + return "Group{" + + "parameter=" + parameter + + ", groupType=" + groupType + + '}'; + } +} diff --git a/me.smartstore/src/domain/group/GroupType.java b/me.smartstore/src/domain/group/GroupType.java new file mode 100644 index 00000000..abaca1dd --- /dev/null +++ b/me.smartstore/src/domain/group/GroupType.java @@ -0,0 +1,24 @@ +package domain.group; + +// None, General, VIP, VVIP +// N, G, V, VV +// 그룹이 가져야 하는 것? 시간과 금액 - 이걸 기준으로 분류한다. + +public enum GroupType { + NONE("N"), GENERAL("G"), VIP("V"), VVIP("VV"); + + private String shortName; + + GroupType(String shortName) { + this.shortName = shortName; + } + + public String getShortName() { + return shortName; + } + + public boolean isName(String input) { + if (this.shortName.equals(input) || this.name().equals(input)) return true; + return false; + } +} diff --git a/me.smartstore/src/domain/group/Groups.java b/me.smartstore/src/domain/group/Groups.java new file mode 100644 index 00000000..c45af28c --- /dev/null +++ b/me.smartstore/src/domain/group/Groups.java @@ -0,0 +1,49 @@ +package domain.group; + +import handler.exception.NoSuchGroupException; +import handler.exception.NullArgumentException; +import util.DArray; + +public class Groups extends DArray { + + // singleton + private static Groups groups; + + public static Groups getInstance() { + if (groups == null) { + // Groups 인스턴스 생성 시 default GroupType NONE을 add. + groups = new Groups( + new Group(new Parameter(0, 0), GroupType.NONE) + ); + } + return groups; + } + + private Groups() {} + + private Groups(Group group) { + this.add(group); + } + + public Group findGroup(GroupType groupType) { + // 그룹 사이즈가 1이면 default로 등록한 NONE 그룹 뿐이므로 예외를 던져서 그룹 등록을 유도 + if (groups.size() == 1) + throw new NoSuchGroupException(); + + try { + int index = 0; + // GroupType NONE은 제외하고 GENERAL부터 탐색 + for (int i = 1; i < groups.size; i++) { + if (groups.get(i).isGroupType(groupType)) { + index = i; + } + } + //검색 결과 index가 1보다 작다면 찾는 그룹이 없다는 뜻이므로 예외를 던져서 그룹 등록을 유도 + if (index < 1) throw new NoSuchGroupException(); + + return groups.get(index); + } catch (IndexOutOfBoundsException | NullArgumentException e) { + throw new NoSuchGroupException(); + } + } +} diff --git a/me.smartstore/src/domain/group/Parameter.java b/me.smartstore/src/domain/group/Parameter.java new file mode 100644 index 00000000..9bfe2b1b --- /dev/null +++ b/me.smartstore/src/domain/group/Parameter.java @@ -0,0 +1,52 @@ +package domain.group; + +import java.util.Objects; + +public class Parameter { + private Integer minTime; + private Integer minPay; + + public Parameter() { } + + public Parameter(Integer minTime, Integer minPay) { + this.minTime = minTime; + this.minPay = minPay; + } + + public Integer getMinTime() { + return minTime; + } + + public void setMinTime(Integer minTime) { + this.minTime = minTime; + } + + public Integer getMinPay() { + return minPay; + } + + public void setMinPay(Integer minPay) { + this.minPay = minPay; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Parameter parameter = (Parameter) o; + return Objects.equals(minTime, parameter.minTime) && Objects.equals(minPay, parameter.minPay); + } + + @Override + public int hashCode() { + return Objects.hash(minTime, minPay); + } + + @Override + public String toString() { + return "Parameter{" + + "minTime=" + minTime + + ", minPay=" + minPay + + '}'; + } +} diff --git a/me.smartstore/src/domain/menu/CustomerMenu.java b/me.smartstore/src/domain/menu/CustomerMenu.java new file mode 100644 index 00000000..b4bf4b8f --- /dev/null +++ b/me.smartstore/src/domain/menu/CustomerMenu.java @@ -0,0 +1,43 @@ +package domain.menu; + +import Service.CustomerService; +import handler.exception.InputFormatException; +import handler.exception.InputRangeException; + +public class CustomerMenu implements Menu { + + private static final CustomerService customerService = CustomerService.getInstance(); + private static CustomerMenu customerMenu; + + public static CustomerMenu getInstance() { + if (customerMenu == null) { + customerMenu = new CustomerMenu(); + } + return customerMenu; + } + + private CustomerMenu() {} + + @Override + public void manage() { + while (true) { + try { + int choice = customerMenu.selectMenu(new String[]{ + "Add Customer Data", + "View Customer Data", + "Update Customer Data", + "Delete Customer Data", + "Back" + }); + + if (choice == 1) customerService.addCustomer(); + else if (choice == 2) customerService.showCustomerAll(); + else if (choice == 3) customerService.updateCustomer(); + else if (choice == 4) customerService.deleteCustomer(); + else if (choice == 5) break; + } catch (InputRangeException | InputFormatException e) { + System.out.println(e.getMessage()); + } + } + } +} \ No newline at end of file diff --git a/me.smartstore/src/domain/menu/GroupMenu.java b/me.smartstore/src/domain/menu/GroupMenu.java new file mode 100644 index 00000000..ad4a9ca8 --- /dev/null +++ b/me.smartstore/src/domain/menu/GroupMenu.java @@ -0,0 +1,42 @@ +package domain.menu; + +import Service.GroupService; +import handler.exception.InputFormatException; +import handler.exception.InputRangeException; + +public class GroupMenu implements Menu { + + private static final GroupService groupService = GroupService.getInstance(); + private static GroupMenu groupMenu; + + public static GroupMenu getInstance() { + if (groupMenu == null) { + groupMenu = new GroupMenu(); + } + return groupMenu; + } + + private GroupMenu() { + } + + @Override + public void manage() { + while (true) { + try { + int choice = groupMenu.selectMenu(new String[]{ + "Set Parameter", + "View Parameter", + "Update Parameter", + "Back" + }); + + if (choice == 1) groupService.setParameter(); + if (choice == 2) groupService.showParameter(); + if (choice == 3) groupService.updateParameter(); + if (choice == 4) break; + } catch (InputRangeException | InputFormatException e) { + System.out.println(e.getMessage()); + } + } + } +} diff --git a/me.smartstore/src/domain/menu/MainMenu.java b/me.smartstore/src/domain/menu/MainMenu.java new file mode 100644 index 00000000..6dacb022 --- /dev/null +++ b/me.smartstore/src/domain/menu/MainMenu.java @@ -0,0 +1,45 @@ +package domain.menu; + +import handler.exception.InputFormatException; +import handler.exception.InputRangeException; + +public class MainMenu implements Menu { + + private static final CustomerMenu customerMenu = CustomerMenu.getInstance(); + private static final GroupMenu groupMenu = GroupMenu.getInstance(); + private static final SummaryMenu summaryMenu = SummaryMenu.getInstance(); + + private static MainMenu mainMenu; + + public static MainMenu getInstance() { + if (mainMenu == null) { + mainMenu = new MainMenu(); + } + return mainMenu; + } + + private MainMenu() {} + + @Override + public void manage() { + while ( true ) { + try { + int choice = mainMenu.selectMenu(new String[] { + "Parameter", + "Customer", + "Classification Summary", + "Quit"}); + + if (choice == 1) groupMenu.manage(); + if (choice == 2) customerMenu.manage(); + if (choice == 3) summaryMenu.manage(); + if (choice == 4) { + System.out.println("Program Finished"); + break; + } + } catch (InputRangeException | InputFormatException e) { + System.out.println(e.getMessage()); + } + } + } +} diff --git a/me.smartstore/src/domain/menu/Menu.java b/me.smartstore/src/domain/menu/Menu.java new file mode 100644 index 00000000..8033e610 --- /dev/null +++ b/me.smartstore/src/domain/menu/Menu.java @@ -0,0 +1,100 @@ +package domain.menu; + +//각각의 메뉴는 객체가 하나만 있나? +//각 메뉴는 싱글톤으로 가야 하나? + +import handler.exception.InputEndException; +import handler.exception.InputFormatException; +import handler.exception.InputRangeException; + +import java.util.NoSuchElementException; +import java.util.Scanner; + +public interface Menu { + + // String menuNumFormat = "^[1-9]*$"; + Scanner sc = new Scanner(System.in); + + default String nextLine() { + return sc.nextLine(); + } + + default String nextLine(String END_MSG) { + System.out.println("** Press 'end', if you want to exit! **"); + String str = sc.nextLine(); + if (str.toUpperCase().equals(END_MSG)) { + throw new InputEndException(); + } + return str; + } + + default String nextLineUpper(String END_MSG) { + System.out.println("** Press 'end', if you want to exit! **"); + String str = sc.nextLine().toUpperCase(); + if (str.equals(END_MSG)) { + throw new InputEndException(); + } + return str; + } + + default int nextInt() { + try { + return Integer.parseInt(sc.nextLine()); + } catch (NumberFormatException e) { + throw new InputFormatException(); + } + } + + default int nextInt(String END_MSG) { + try { + System.out.println("** Press 'end', if you want to exit! **"); + String str = sc.nextLine().toUpperCase(); + + if (str.equals(END_MSG)) { + throw new InputEndException(); + } + return Integer.parseInt(str); + } catch (NumberFormatException e) { + throw new InputFormatException(); + } + } + + /** + * 메뉴 번호 체크. + * + * @param choice : 사용자가 입력한 메뉴 번호 + * @param limit : 메뉴의 마지막 번호(메뉴 텍스트 배열의 길이) + * @return : true - 범위를 벗어남 / false - 범위 내 + */ + default boolean isInvalidRange(int choice, int limit) { + return (choice < 1 || choice > limit); + } + + default void showMenu(String[] menus) { + System.out.println("\n==============================="); + for (int i = 0; i < menus.length; i++) { + System.out.printf(" %d. %s\n", i + 1, menus[i]); + } + System.out.println("==============================="); + System.out.print("Choose One: "); + } + + default int selectMenu(String[] menus) { + while (true) { + try { + showMenu(menus); + int choice = nextInt(); + //사용자가 입력한 메뉴번호가 범위에서 벗어나면 Exception을 발생시킨다. + if (isInvalidRange(choice, menus.length)) { + throw new InputRangeException(); + } + return choice; + + } catch (NoSuchElementException | IllegalStateException | NumberFormatException e) { + throw new InputFormatException(); + } + } + } + + void manage(); +}//end of class diff --git a/me.smartstore/src/domain/menu/SummaryMenu.java b/me.smartstore/src/domain/menu/SummaryMenu.java new file mode 100644 index 00000000..5b26e9db --- /dev/null +++ b/me.smartstore/src/domain/menu/SummaryMenu.java @@ -0,0 +1,82 @@ +package domain.menu; + +import Service.SummaryService; +import handler.exception.InputEmptyException; +import handler.exception.InputEndException; +import handler.exception.InputFormatException; +import handler.exception.InputRangeException; + +import java.util.function.Predicate; + +import static resources.Message.END_MSG; + +public class SummaryMenu implements Menu { + + private static final SummaryService summaryService = SummaryService.getInstance(); + private static SummaryMenu summaryMenu; + + public static SummaryMenu getInstance() { + if (summaryMenu == null) { + summaryMenu = new SummaryMenu(); + } + return summaryMenu; + } + + private SummaryMenu() {} + + @Override + public void manage() { + while (true) { + try { + int choice = summaryMenu.selectMenu(new String[]{ + "Summary", + "Summary (Sorted By Name)", + "Summary (Sorted By Time)", + "Summary (Sorted By Total Payment)", + "Back" + }); + + if (choice == 1) summaryService.showDefaultSummary(); + if (choice == 2) summaryService.showByName(getSortOrder()); + if (choice == 3) summaryService.showByTime(getSortOrder()); + if (choice == 4) summaryService.showByPayment(getSortOrder()); + if (choice == 5) break; + } catch (InputRangeException | InputFormatException e) { + System.out.println(e.getMessage()); + } + } + } + + /** + * @return sortOrder - ASC : false / DESC : ture + * @throws InputEndException + */ + private boolean getSortOrder() { + while (true) { + try { + System.out.println("Which order (ASCENDING (A), DESCENDING (D))?"); + String order = summaryMenu.nextLineUpper(END_MSG); + return isOrderName.test(order); + } catch (InputEmptyException | InputFormatException | InputRangeException e) { + System.out.println(e.getMessage()); + } catch (InputEndException e) { + System.out.println(e.getMessage()); + break; + } + } + throw new InputRangeException(); + } + + Predicate isOrderName = input -> { + if (input.isEmpty()) { + throw new InputEmptyException(); + } + if (input.equals("A")) { + return false; + } + if (input.equals("D")) { + return true; + } + throw new InputRangeException(); + }; +} diff --git a/me.smartstore/src/handler/exception/ArrayEmptyException.java b/me.smartstore/src/handler/exception/ArrayEmptyException.java new file mode 100644 index 00000000..57c49e3b --- /dev/null +++ b/me.smartstore/src/handler/exception/ArrayEmptyException.java @@ -0,0 +1,13 @@ +package handler.exception; + +import resources.Message; + +public class ArrayEmptyException extends RuntimeException { + public ArrayEmptyException() { + super(Message.ERR_MSG_INVALID_ARR_EMPTY); + } + + public ArrayEmptyException(String message) { + super(message); + } +} diff --git a/me.smartstore/src/handler/exception/ElementNotFoundException.java b/me.smartstore/src/handler/exception/ElementNotFoundException.java new file mode 100644 index 00000000..f7e1c71f --- /dev/null +++ b/me.smartstore/src/handler/exception/ElementNotFoundException.java @@ -0,0 +1,23 @@ +package handler.exception; + +import resources.Message; + +public class ElementNotFoundException extends RuntimeException { + public ElementNotFoundException() { super(Message.ERR_MSG_NULL_ARR_ELEMENT); } + + public ElementNotFoundException(String message) { + super(message); + } + + public ElementNotFoundException(String message, Throwable cause) { + super(message, cause); + } + + public ElementNotFoundException(Throwable cause) { + super(cause); + } + + public ElementNotFoundException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/me.smartstore/src/handler/exception/EmptyArrayException.java b/me.smartstore/src/handler/exception/EmptyArrayException.java new file mode 100644 index 00000000..3fdd2d53 --- /dev/null +++ b/me.smartstore/src/handler/exception/EmptyArrayException.java @@ -0,0 +1,22 @@ +package handler.exception; + +public class EmptyArrayException extends RuntimeException { + public EmptyArrayException() { + } + + public EmptyArrayException(String message) { + super(message); + } + + public EmptyArrayException(String message, Throwable cause) { + super(message, cause); + } + + public EmptyArrayException(Throwable cause) { + super(cause); + } + + public EmptyArrayException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/me.smartstore/src/handler/exception/InputEmptyException.java b/me.smartstore/src/handler/exception/InputEmptyException.java new file mode 100644 index 00000000..f0aae36f --- /dev/null +++ b/me.smartstore/src/handler/exception/InputEmptyException.java @@ -0,0 +1,13 @@ +package handler.exception; + +import resources.Message; + +public class InputEmptyException extends RuntimeException { + public InputEmptyException() { + super(Message.ERR_MSG_INVALID_INPUT_EMPTY); + } + + public InputEmptyException(String message) { + super(message); + } +} diff --git a/me.smartstore/src/handler/exception/InputEndException.java b/me.smartstore/src/handler/exception/InputEndException.java new file mode 100644 index 00000000..02f5cd83 --- /dev/null +++ b/me.smartstore/src/handler/exception/InputEndException.java @@ -0,0 +1,13 @@ +package handler.exception; + +import resources.Message; + +public class InputEndException extends RuntimeException { + public InputEndException() { + super(Message.ERR_MSG_INPUT_END); + } + + public InputEndException(String message) { + super(message); + } +} diff --git a/me.smartstore/src/handler/exception/InputFormatException.java b/me.smartstore/src/handler/exception/InputFormatException.java new file mode 100644 index 00000000..f939e92d --- /dev/null +++ b/me.smartstore/src/handler/exception/InputFormatException.java @@ -0,0 +1,13 @@ +package handler.exception; + +import resources.Message; + +public class InputFormatException extends RuntimeException { + public InputFormatException() { + super(Message.ERR_MSG_INVALID_INPUT_FORMAT); + } + + public InputFormatException(String message) { + super(message); + } +} diff --git a/me.smartstore/src/handler/exception/InputRangeException.java b/me.smartstore/src/handler/exception/InputRangeException.java new file mode 100644 index 00000000..08cd0ab0 --- /dev/null +++ b/me.smartstore/src/handler/exception/InputRangeException.java @@ -0,0 +1,13 @@ +package handler.exception; + +import resources.Message; + +public class InputRangeException extends RuntimeException { + public InputRangeException() { + super(Message.ERR_MSG_INVALID_INPUT_RANGE); + } + + public InputRangeException(String message) { + super(message); + } +} diff --git a/me.smartstore/src/handler/exception/InputTypeException.java b/me.smartstore/src/handler/exception/InputTypeException.java new file mode 100644 index 00000000..f1895ace --- /dev/null +++ b/me.smartstore/src/handler/exception/InputTypeException.java @@ -0,0 +1,13 @@ +package handler.exception; + +import resources.Message; + +public class InputTypeException extends RuntimeException { + public InputTypeException() { + super(Message.ERR_MSG_INVALID_INPUT_TYPE); + } + + public InputTypeException(String message) { + super(message); + } +} diff --git a/me.smartstore/src/handler/exception/InvalidIdException.java b/me.smartstore/src/handler/exception/InvalidIdException.java new file mode 100644 index 00000000..cacdbc0c --- /dev/null +++ b/me.smartstore/src/handler/exception/InvalidIdException.java @@ -0,0 +1,13 @@ +package handler.exception; + +import resources.Message; + +public class InvalidIdException extends RuntimeException { + public InvalidIdException() { + super(Message.ERR_MSG_DUPLICATE_ID); + } + + public InvalidIdException(String message) { + super(message); + } +} diff --git a/me.smartstore/src/handler/exception/NoSuchGroupException.java b/me.smartstore/src/handler/exception/NoSuchGroupException.java new file mode 100644 index 00000000..467ca4ab --- /dev/null +++ b/me.smartstore/src/handler/exception/NoSuchGroupException.java @@ -0,0 +1,13 @@ +package handler.exception; + +import static resources.Message.ERR_MSG_INVALID_GROUP_EMPTY; + +public class NoSuchGroupException extends RuntimeException { + public NoSuchGroupException() { + super(ERR_MSG_INVALID_GROUP_EMPTY); + } + + public NoSuchGroupException(String message) { + super(message); + } +} diff --git a/me.smartstore/src/handler/exception/NullArgumentException.java b/me.smartstore/src/handler/exception/NullArgumentException.java new file mode 100644 index 00000000..0fe1bd07 --- /dev/null +++ b/me.smartstore/src/handler/exception/NullArgumentException.java @@ -0,0 +1,24 @@ +package handler.exception; + +import static resources.Message.ERR_MSG_NULL_ARR_ELEMENT; + +public class NullArgumentException extends RuntimeException { + public NullArgumentException() { + super(ERR_MSG_NULL_ARR_ELEMENT); + } + public NullArgumentException(String message) { + super(message); + } + + public NullArgumentException(String message, Throwable cause) { + super(message, cause); + } + + public NullArgumentException(Throwable cause) { + super(cause); + } + + public NullArgumentException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/me.smartstore/src/resources/Message.java b/me.smartstore/src/resources/Message.java new file mode 100644 index 00000000..d7417b6d --- /dev/null +++ b/me.smartstore/src/resources/Message.java @@ -0,0 +1,17 @@ +package resources; + +public interface Message { + + String ERR_MSG_INVALID_ARR_EMPTY = "No Customers. Please input one first."; + String ERR_MSG_NULL_ARR_ELEMENT = "Elements in Array has null. Array can't be sorted."; + String ERR_MSG_INVALID_INPUT_NULL = "Null Input. Please input something."; + String ERR_MSG_INVALID_INPUT_EMPTY = "Empty Input. Please input something."; + String ERR_MSG_INVALID_INPUT_RANGE = "Out of range for input. Please try again."; + String ERR_MSG_INVALID_INPUT_TYPE = "Invalid Type for Input. Please try again."; + String ERR_MSG_INVALID_INPUT_FORMAT = "Invalid Format for Input. Please try again."; + String ERR_MSG_INPUT_END = "END is pressed. Exit this domain.menu."; + String ERR_MSG_DUPLICATE_ID = "Input id is Duplicated. Please input different id."; + String ERR_MSG_INVALID_GROUP_EMPTY = "[ERROR] => Group does not exist. Please input one first."; + String ERR_MSG_GROUP_CUSTOMER_EMPTY = "There are no customers in this group."; + String END_MSG = "END"; +} diff --git a/me.smartstore/src/test/TestCase.java b/me.smartstore/src/test/TestCase.java new file mode 100644 index 00000000..69fd6598 --- /dev/null +++ b/me.smartstore/src/test/TestCase.java @@ -0,0 +1,60 @@ +package test; + +import Service.CustomerService; +import domain.customer.Customer; +import domain.customer.Customers; +import domain.group.Group; +import domain.group.GroupType; +import domain.group.Groups; +import domain.group.Parameter; + +import java.util.Random; + +public class TestCase { + + private static final Groups groups = Groups.getInstance(); + private static final Customers customers = Customers.getInstance(); + private static final CustomerService customerService = CustomerService.getInstance(); + + public void buildTestCase() { + groups.add(new Group(new Parameter(10, 100000), GroupType.GENERAL)); + groups.add(new Group(new Parameter(20, 200000), GroupType.VIP)); + groups.add(new Group(new Parameter(30, 300000), GroupType.VVIP)); + + for (int i = 0; i < 26; i++) { + customers.add(new Customer( + Character.toString( + (char) ('a' + i)), + (char) ('a' + i) + "123", + ((int) (Math.random() * 5) + 1) * 10, + ((int) (Math.random() * 5) + 1) * 100000)); + } + + //NONE 타입 테스트를 위한 Customer generate + int startChar = 48; // number '0' + int endChar = 122; // letter 'z' + int targetLength = 10; + Random random = new Random(); + + for (int x = 0; x < 5; x++) { + String randomName = random.ints(startChar,endChar + 1) + .filter(i -> (i <= 57 || i >= 65) && (i <= 90 || i >= 97)) + .limit(targetLength) + .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) + .toString(); + + String randomId = random.ints(startChar,endChar + 1) + .filter(i -> (i <= 57 || i >= 65) && (i <= 90 || i >= 97)) + .limit(targetLength) + .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) + .toString(); + + customers.add(new Customer(randomName, randomId, 0, 0)); + } + + customerService.refresh(); + + System.out.println("allCustomers = \n" + customers); + System.out.println("allGroups = \n" + groups); + } +} diff --git a/me.smartstore/src/util/Collections.java b/me.smartstore/src/util/Collections.java new file mode 100644 index 00000000..be715b3f --- /dev/null +++ b/me.smartstore/src/util/Collections.java @@ -0,0 +1,16 @@ +package util; + +public interface Collections { + // 데이터를 가지고 있는 객체가 아님 + // 구현 해야하는 메소드의 정보만 가지고 있음 (인터페이스) + + int size(); + T get(int index); + void set(int index, T object); + int indexOf(T object); + void add(T object); + void add(int index, T object); + T pop(); + T pop(int index); + T pop(T object); +} diff --git a/me.smartstore/src/util/DArray.java b/me.smartstore/src/util/DArray.java new file mode 100644 index 00000000..1fcf14fd --- /dev/null +++ b/me.smartstore/src/util/DArray.java @@ -0,0 +1,150 @@ +package util; + + +import handler.exception.ElementNotFoundException; +import handler.exception.EmptyArrayException; +import handler.exception.NullArgumentException; + +public class DArray implements Collections { // Dynamic Array + + //static 필드는 가장 위로 올려주는 게 좋다. + protected static final int DEFAULT = 10; + + protected T[] arrays; + + protected int size; + protected int capacity; + + public DArray() throws ClassCastException { + arrays = (T[]) new Object[DEFAULT]; + capacity = DEFAULT; + } + + public DArray(int initial) throws ClassCastException { + arrays = (T[]) new Object[initial]; + capacity = initial; + } + + public DArray(T[] arrays) { + this.arrays = arrays; + capacity = arrays.length; + size = arrays.length; + } + + ///////////////////////////////////////// + // add, set, get, pop, indexOf, size, capacity (for dynamic-sized array) + + @Override + public int size() { + return size; + } + + // 배열에 얼마나 capacity 남아있는지 외부에 알려줄 필요가 없기 때문에 으로 정의 + protected int capacity() { + return capacity; + } + + @Override + public T get(int index) throws IndexOutOfBoundsException { + if (index < 0 || index >= size) throw new IndexOutOfBoundsException(); + return arrays[index]; + } + + @Override + public void set(int index, T object) throws IndexOutOfBoundsException, NullArgumentException { + if (index < 0 || index >= size) throw new IndexOutOfBoundsException(); + if (object == null) throw new NullArgumentException(); + + arrays[index] = object; + } + + @Override + public int indexOf(T object) throws NullArgumentException, ElementNotFoundException { + if (object == null) throw new NullArgumentException(); // not found (instead of throwing handler.exception) + + for (int i = 0; i < size; i++) { + if (arrays[i] == null) continue; + if (arrays[i].equals(object)) return i; + } + throw new ElementNotFoundException(); // not found + } + + // 배열의 capacity가 부족한 경우 + @Override + public void add(T object) throws NullArgumentException { + if (object == null) throw new NullArgumentException(); // if argument is null, do not add null value in array + + if (size < capacity) { + arrays[size] = object; + size++; + } else { + extend(); + add(object); + } + } + + @Override + public void add(int index, T object) throws IndexOutOfBoundsException, NullArgumentException { + if (index < 0 || index >= size) throw new IndexOutOfBoundsException(); + if (object == null) throw new NullArgumentException(); + + if (size < capacity) { + for (int i = size - 1; i >= index; i--) { + arrays[i + 1] = arrays[i]; + } + arrays[index] = object; + size++; + } else { + extend(); + add(index, object); + } + } + + @Override + public T pop() { +// if (size == 0) return null; +// +// T popElement = arrays[size-1]; +// arrays[size-1] = null; +// size--; +// return popElement; + return pop(size - 1); + } + + @Override + public T pop(int index) throws IndexOutOfBoundsException { + if (size == 0) throw new EmptyArrayException(); + if (index < 0 || index >= size) throw new IndexOutOfBoundsException(); + + T popElement = arrays[index]; + arrays[index] = null; // 삭제됨을 명시적으로 표현 + + for (int i = index + 1; i < size; i++) { + arrays[i - 1] = arrays[i]; + } + arrays[size - 1] = null; + size--; + return popElement; + } + + @Override + public T pop(T object) { + return pop(indexOf(object)); + } + + protected void extend() { + capacity *= 2; // doubling + arrays = java.util.Arrays.copyOf(arrays, capacity); + + // size는 그대로 + } + + @Override + public String toString() { + StringBuilder toStr = new StringBuilder(); + for (int i = 0; i < size; i++) { + toStr.append(arrays[i]).append("\n"); + } + return toStr.toString(); + } +}