diff --git a/.idea/vcs.xml b/.idea/vcs.xml index d843f34..35eb1dd 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,4 +1,6 @@ - + + + \ No newline at end of file diff --git a/src/main/java/org/sopt/seminar1/Diary.java b/src/main/java/org/sopt/seminar1/Diary.java new file mode 100644 index 0000000..c56b161 --- /dev/null +++ b/src/main/java/org/sopt/seminar1/Diary.java @@ -0,0 +1,38 @@ +package org.sopt.seminar1; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +public class Diary { + private Long id; + private final String body; + private boolean isDeleted = false; + private LocalDateTime lastEditedDateTime; + + public Diary(Long id, String body) { + this.id = id; + this.body = body; + this.lastEditedDateTime = LocalDateTime.now(); + } + + public Long getId() { + return id; + } + public String getBody() { + return body; + } + + public boolean isDeleted() { + return isDeleted; + } + public void setDeleted(boolean deleted) { + this.isDeleted = deleted; + } + + public LocalDateTime getLastEditedDateTime() { + return lastEditedDateTime; + } + public void setLastEditedDateTime(LocalDateTime lastEditedDateTime) { + this.lastEditedDateTime = lastEditedDateTime; + } +} diff --git a/src/main/java/org/sopt/seminar1/DiaryController.java b/src/main/java/org/sopt/seminar1/DiaryController.java new file mode 100644 index 0000000..a1b71cf --- /dev/null +++ b/src/main/java/org/sopt/seminar1/DiaryController.java @@ -0,0 +1,63 @@ +package org.sopt.seminar1; + +import java.util.List; + +public class DiaryController { + private Status status = Status.READY; + private final DiaryService diaryService = new DiaryService(); + + Status getStatus() { + return status; + } + + void boot() { + this.status = Status.RUNNING; + } + + void finish() { + this.status = Status.FINISHED; + } + + // APIS + final List getList() { + return diaryService.getDiaryList(); + } + + final void post(final String body) { + diaryService.writeDiary(body); + } + + final void delete(final String id) { + long longId = validateAndConvertId(id); + diaryService.deleteDiary(longId); + } + + final void patch(final String id, final String body) { + long longId = validateAndConvertId(id); + diaryService.updateDiary(longId, body); + } + + final void restore(final String id) { + long longId = validateAndConvertId(id); + diaryService.restoreDiary(longId); + } + + enum Status { + READY, + RUNNING, + FINISHED, + ERROR, + } + + private long validateAndConvertId(final String id) { + if (!id.matches("^[1-9][0-9]*$")) { + throw new Main.UI.InvalidInputException(); + } + + try { + return Long.parseLong(id); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("유효한 long 형식의 ID여야 합니다."); + } + } +} diff --git a/src/main/java/org/sopt/seminar1/DiaryRepository.java b/src/main/java/org/sopt/seminar1/DiaryRepository.java new file mode 100644 index 0000000..6cd51c7 --- /dev/null +++ b/src/main/java/org/sopt/seminar1/DiaryRepository.java @@ -0,0 +1,67 @@ +package org.sopt.seminar1; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + +public class DiaryRepository { + private final Map storage = new ConcurrentHashMap<>(); + private final AtomicLong numbering = new AtomicLong(); + private int updateCount = 0; + + void save(Diary diary) { + final long id = numbering.addAndGet(1); + diary.setDeleted(false); + diary = new Diary(id, diary.getBody()); + storage.put(id, diary); // Diary 객체를 저장 + } + + List findAll() { + final List diaryList = new ArrayList<>(); + for (long index = 1; index <= numbering.longValue(); index++) { + final Diary diary = storage.get(index); + if (diary != null && !diary.isDeleted()) { + diaryList.add(diary); + } + } + return diaryList; + } + + void delete(final long id) { + Diary diary = storage.get(id); + if (diary != null) { + diary.setDeleted(true); + } + } + + void update(final long id, final String newBody) { + Diary diary = storage.get(id); + if (diary != null && !diary.isDeleted()) { + LocalDateTime now = LocalDateTime.now(); + LocalDateTime lastEditedDateTime = diary.getLastEditedDateTime(); + + if (!(lastEditedDateTime.toLocalDate().equals(now.toLocalDate()))) { + updateCount = 0; + diary.setLastEditedDateTime(now); + } + + if (updateCount <2){ + updateCount++; + diary.setDeleted(false); + storage.replace(id, new Diary(id, newBody)); + } else { + throw new Main.UI.InvalidInputException(); + } + } + } + + void restore(final long id) { + Diary diary = storage.get(id); + if (diary != null && diary.isDeleted()) { + diary.setDeleted(false); + } + } +} diff --git a/src/main/java/org/sopt/seminar1/DiaryService.java b/src/main/java/org/sopt/seminar1/DiaryService.java new file mode 100644 index 0000000..b95e646 --- /dev/null +++ b/src/main/java/org/sopt/seminar1/DiaryService.java @@ -0,0 +1,24 @@ +package org.sopt.seminar1; + +import java.util.List; + +public class DiaryService { + private final DiaryRepository diaryRepository = new DiaryRepository(); + + void writeDiary(String body){ + final Diary diary = new Diary(null, body); + diaryRepository.save(diary); + } + List getDiaryList(){ + return diaryRepository.findAll(); + } + void deleteDiary(long id){ + diaryRepository.delete(id); + } + void updateDiary(long id, String body){ + diaryRepository.update(id ,body); + } + void restoreDiary(long id) { // 복구 기능 추가 + diaryRepository.restore(id); + } +} diff --git a/src/main/java/org/sopt/seminar1/Main.java b/src/main/java/org/sopt/seminar1/Main.java new file mode 100644 index 0000000..b49025b --- /dev/null +++ b/src/main/java/org/sopt/seminar1/Main.java @@ -0,0 +1,188 @@ +package org.sopt.seminar1; + +import java.io.*; + +public class Main { + public static void main(String[] args) { + final UI ui; + try { + ui = new DiaryUI(new DiaryController()); + ui.runRepeatedly(); + } catch (Throwable t) { + + } + } + + interface UI { + void runRepeatedly() throws IOException; + + class UIException extends RuntimeException { + } + + class InvalidInputException extends UIException { + } + } + + static class DiaryUI implements UI { + private final DiaryController server; + private String selected; + + public DiaryUI(DiaryController server) throws IOException { + this.server = server; + server.boot(); + ConsoleIO.printLine(getStartMessage()); + } + + public void runRepeatedly() throws IOException { + + do { + if (onMenu()) { + ConsoleIO.printLine(""); + ConsoleIO.printLine(getMenu()); + selected = ConsoleIO.readLine().trim().toUpperCase(); + } + + try { + run(); + } catch (InvalidInputException e) { + ConsoleIO.printLine("잘못된 값을 입력하였습니다."); + } + + if (isFinished()) { + ConsoleIO.printLine(getFinishMessage()); + break; + } + + selected = null; + } while (isRunning()); + } + + //30글자 제한 + private void validateInputLength(final String input) throws InvalidInputException, IOException { + if (input.length() > 30) { + ConsoleIO.printLine("일기는 최대 30글자까지만 입력할 수 있습니다. 다시 입력해주세요."); + throw new InvalidInputException(); + } + } + + private void run() throws IOException { + switch (server.getStatus()) { + case READY, FINISHED, ERROR -> throw new UIException(); + + case RUNNING -> { + switch (selected) { + case "GET" -> { + server.getList().forEach(diary -> { + try { + ConsoleIO.printLine(diary.getId()+" : "+diary.getBody()); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + case "POST" -> { + ConsoleIO.printLine("한 줄 일기를 작성해주세요!"); + final String input = ConsoleIO.readLine(); + validateInputLength(input); + server.post(input); + } + + case "DELETE" -> { + ConsoleIO.printLine("삭제할 id 를 입력하세요!"); + final String input = ConsoleIO.readLine(); + server.delete(input); + } + case "PATCH" -> { + ConsoleIO.printLine("수정할 id 를 입력하세요!"); + final String inputId = ConsoleIO.readLine(); + + ConsoleIO.printLine("수정 body 를 입력하세요!"); + final String inputBody = ConsoleIO.readLine(); + validateInputLength(inputBody); + server.patch(inputId, inputBody); + } + case "RESTORE" -> { + ConsoleIO.printLine("복구할 id 를 입력하세요!"); + final String input = ConsoleIO.readLine(); + server.restore(input); // 복구 기능 호출 + } + case "FINISH" -> { + server.finish(); + } + default -> { + throw new InvalidInputException(); + } + } + } + + } + } + + private boolean isRunning() { + return server.getStatus() == DiaryController.Status.RUNNING; + } + + private boolean isFinished() { + return server.getStatus() == DiaryController.Status.FINISHED; + } + + private boolean onMenu() { + return selected == null; + } + + private String getMenu() { + return """ + ============================ + - GET : 일기 불러오기 + - POST : 일기 작성하기 + - DELETE : 일기 제거하기 + - PATCH : 일기 수정하기 + - RESTORE : 일기 복구하기 + """; + + } + + private String getStartMessage() { + return "시작합니다 :)"; + } + + private String getFinishMessage() { + return "종료됩니다 :)"; + } + } + + // not thread safe + private static class ConsoleIO { + private final static BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out)); + private final static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); + private final static StringBuilder sb = new StringBuilder(); + + public static void printLine(final String toPrint) throws IOException { + if (toPrint == null) { + throw new IllegalArgumentException("console can not print null"); + } + + appendLine(toPrint); + print(); + clearStringBuilder(); + } + + public static String readLine() throws IOException { + return bufferedReader.readLine(); + } + + private static void appendLine(final String toPrint) { + sb.append(toPrint); + sb.append("\n"); + } + + private static void print() throws IOException { + bufferedWriter.write(sb.toString()); + bufferedWriter.flush(); + } + + private static void clearStringBuilder() { + sb.setLength(0); + } + } +}