diff --git a/board/.gitattributes b/board/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/board/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/board/.gitignore b/board/.gitignore new file mode 100644 index 0000000..7f4db33 --- /dev/null +++ b/board/.gitignore @@ -0,0 +1,38 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ +/db/ diff --git a/board/build.gradle b/board/build.gradle new file mode 100644 index 0000000..3d943b3 --- /dev/null +++ b/board/build.gradle @@ -0,0 +1,31 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.4.1' + id 'io.spring.dependency-management' version '1.1.7' +} + +group = 'com.programmers' +version = '0.0.1-SNAPSHOT' + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + testImplementation("org.assertj:assertj-core:3.26.3") + + implementation 'com.fasterxml.jackson.core:jackson-databind:2.12.4'} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/board/gradle/wrapper/gradle-wrapper.jar b/board/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..a4b76b9 Binary files /dev/null and b/board/gradle/wrapper/gradle-wrapper.jar differ diff --git a/board/gradle/wrapper/gradle-wrapper.properties b/board/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..e2847c8 --- /dev/null +++ b/board/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/board/gradlew b/board/gradlew new file mode 100644 index 0000000..f5feea6 --- /dev/null +++ b/board/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/board/gradlew.bat b/board/gradlew.bat new file mode 100644 index 0000000..9d21a21 --- /dev/null +++ b/board/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/board/settings.gradle b/board/settings.gradle new file mode 100644 index 0000000..d0111f5 --- /dev/null +++ b/board/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'devcourse' diff --git a/board/src/main/java/com/programmers/devcourse/LectureApplication.java b/board/src/main/java/com/programmers/devcourse/LectureApplication.java new file mode 100644 index 0000000..9cea226 --- /dev/null +++ b/board/src/main/java/com/programmers/devcourse/LectureApplication.java @@ -0,0 +1,18 @@ +package com.programmers.devcourse; + +import com.programmers.devcourse.controller.SayingController; +import com.programmers.devcourse.view.InputView; +import com.programmers.devcourse.view.JsonDBInputView; +import com.programmers.devcourse.view.OutputView; + +import java.io.IOException; + +public class LectureApplication { + public static void main(String[] args) throws IOException { + InputView inputView = new InputView(); + OutputView outputView = new OutputView(); + + SayingController sayingController = new SayingController(inputView, outputView); + sayingController.run(); + } +} diff --git a/board/src/main/java/com/programmers/devcourse/controller/SayingCommander.java b/board/src/main/java/com/programmers/devcourse/controller/SayingCommander.java new file mode 100644 index 0000000..21418f6 --- /dev/null +++ b/board/src/main/java/com/programmers/devcourse/controller/SayingCommander.java @@ -0,0 +1,66 @@ +package com.programmers.devcourse.controller; + +import com.programmers.devcourse.model.Board; +import com.programmers.devcourse.model.CommandType; +import com.programmers.devcourse.model.Saying; +import com.programmers.devcourse.view.InputView; +import com.programmers.devcourse.view.JsonDBOutputView; +import com.programmers.devcourse.view.OutputView; + +import java.io.IOException; + +import static com.programmers.devcourse.model.CommandType.*; + +public class SayingCommander { + private final InputView inputView; + private final OutputView outputView; + + public SayingCommander(InputView inputView, OutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + } + + public boolean command(Board board) throws IOException { + String command = inputView.command(); + CommandType commandType = CommandType.defineCommand(command); + if (commandType.equals(REGISTER)) { + Saying saying = inputView.inputSaying(); + long boardNumber = board.add(saying); + outputView.printRegisterNumber(boardNumber); + return true; + } else if (commandType.equals(LIST)) { + outputView.printOptions(); + outputView.printSayingInfo(board); + return true; + } else if (commandType.equals(REMOVE)) { + String id = command.substring(6); + if (!board.isExist(Long.valueOf(id))) { + outputView.printNotExistSayingNumber(id); + return true; + } + board.remove(Long.parseLong(id)); + outputView.printRemove(id); + return true; + } else if (commandType.equals(MODIFY)) { + String id = command.substring(6); + if (!board.isExist(Long.valueOf(id))) { + outputView.printNotExistSayingNumber(id); + return true; + } + Saying element = board.getElement(Long.parseLong(id)); + outputView.printOriginalSayingContent(element.getContents()); + String newContent = inputView.inputNewContent(); + outputView.printOriginalSayingAuthor(element.getAuthor()); + String newAuthor = inputView.inputNewAuthor(); + Saying modifySaying = element.modify(newAuthor, newContent); + board.modify(Long.parseLong(id), modifySaying); + return true; + } else if (commandType.equals(BUILD)) { + JsonDBOutputView.saveSaying(board); + outputView.printBuildSuccess(); + JsonDBOutputView.lastSayingNumber(board.getLastBoarNumber()); + return true; + } + return false; + } +} diff --git a/board/src/main/java/com/programmers/devcourse/controller/SayingController.java b/board/src/main/java/com/programmers/devcourse/controller/SayingController.java new file mode 100644 index 0000000..b516859 --- /dev/null +++ b/board/src/main/java/com/programmers/devcourse/controller/SayingController.java @@ -0,0 +1,34 @@ +package com.programmers.devcourse.controller; + +import com.programmers.devcourse.model.Board; +import com.programmers.devcourse.model.Saying; +import com.programmers.devcourse.view.InputView; +import com.programmers.devcourse.view.JsonDBInputView; +import com.programmers.devcourse.view.OutputView; + +import java.io.IOException; +import java.util.Map; + +public class SayingController { + private final InputView inputView; + private final OutputView outputView; + + public SayingController(InputView inputView, OutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + } + + public void run() throws IOException { + outputView.printInit(); + Map savedSayingMap = JsonDBInputView.readSaying(); + Long lastId = JsonDBInputView.readLastId(); + SayingCommander sayingCommander = new SayingCommander(inputView, outputView); + Board board = new Board(savedSayingMap, lastId); + while (true) { + boolean isContinue = sayingCommander.command(board); + if (!isContinue) { + break; + } + } + } +} diff --git a/board/src/main/java/com/programmers/devcourse/model/Board.java b/board/src/main/java/com/programmers/devcourse/model/Board.java new file mode 100644 index 0000000..a3e7f08 --- /dev/null +++ b/board/src/main/java/com/programmers/devcourse/model/Board.java @@ -0,0 +1,57 @@ +package com.programmers.devcourse.model; + +import java.util.*; + +public class Board { + private final Map element; + private Long idx = 1L; + + public Board() { + this.element = new TreeMap<>(new Comparator() { + @Override + public int compare(Long o1, Long o2) { + return o2.compareTo(o1); + } + }); + } + + public Board(MapsavedElements,Long lastIdx){ + this.element = savedElements; + this.idx = lastIdx; + } + + public long add(Saying saying) { + element.putIfAbsent(idx, saying); + return idx++; + } + + public Map getElementsMap() { + return this.element; + } + + public Saying getElement(Long id) { + return this.element.get(id); + } + + public int getBoardSize() { + return element.size(); + } + public Long getLastBoarNumber(){ + return idx - 1; + } + public void remove(Long id) { + element.remove(id); + } + + public boolean isExist(Long id) { + if (!element.containsKey(id)) { + return false; + } + return true; + } + + + public void modify(Long id, Saying saying) { + element.put(id, saying); + } +} diff --git a/board/src/main/java/com/programmers/devcourse/model/CommandType.java b/board/src/main/java/com/programmers/devcourse/model/CommandType.java new file mode 100644 index 0000000..994e09b --- /dev/null +++ b/board/src/main/java/com/programmers/devcourse/model/CommandType.java @@ -0,0 +1,20 @@ +package com.programmers.devcourse.model; + +public enum CommandType { + REGISTER,EXIT,LIST, MODIFY, BUILD, REMOVE; + + public static CommandType defineCommand(String command) { + if (command.equals("등록")) { + return CommandType.REGISTER; + } else if (command.equals("목록")) { + return CommandType.LIST; + } else if (command.startsWith("삭제")) { + return CommandType.REMOVE; + } else if (command.startsWith("수정")) { + return CommandType.MODIFY; + }else if(command.equals("빌드")){ + return CommandType.BUILD; + } + return CommandType.EXIT; + } +} diff --git a/board/src/main/java/com/programmers/devcourse/model/Saying.java b/board/src/main/java/com/programmers/devcourse/model/Saying.java new file mode 100644 index 0000000..b02244a --- /dev/null +++ b/board/src/main/java/com/programmers/devcourse/model/Saying.java @@ -0,0 +1,25 @@ +package com.programmers.devcourse.model; + + +public class Saying { + private String author; + private String contents; + public String getAuthor() { + return author; + } + + public String getContents() { + return contents; + } + + public Saying(String author, String contents) { + this.author = author; + this.contents = contents; + } + + public Saying modify(String newAuthor,String newContents){ + this.author = newAuthor; + this.contents = newContents; + return this; + } +} diff --git a/board/src/main/java/com/programmers/devcourse/view/InputView.java b/board/src/main/java/com/programmers/devcourse/view/InputView.java new file mode 100644 index 0000000..48202ad --- /dev/null +++ b/board/src/main/java/com/programmers/devcourse/view/InputView.java @@ -0,0 +1,34 @@ +package com.programmers.devcourse.view; + +import com.programmers.devcourse.model.Saying; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +public class InputView { + private final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + + public String command() throws IOException { + System.out.print("명령) "); + return br.readLine(); + } + + public Saying inputSaying() throws IOException { + System.out.print("명언 : "); + String contents = br.readLine(); + System.out.print("작가 : "); + String author = br.readLine(); + return new Saying(author,contents); + } + + public String inputNewContent() throws IOException { + System.out.print("명언 : "); + return br.readLine(); + } + + public String inputNewAuthor() throws IOException { + System.out.print("작가 : "); + return br.readLine(); + } +} diff --git a/board/src/main/java/com/programmers/devcourse/view/JsonDBInputView.java b/board/src/main/java/com/programmers/devcourse/view/JsonDBInputView.java new file mode 100644 index 0000000..8489909 --- /dev/null +++ b/board/src/main/java/com/programmers/devcourse/view/JsonDBInputView.java @@ -0,0 +1,65 @@ +package com.programmers.devcourse.view; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.programmers.devcourse.model.Saying; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +public class JsonDBInputView { + public static Map readSaying() throws IOException { + String directoryPath = "db/wiseSaying/"; + File directory = new File(directoryPath); + + File[] files = directory.listFiles((dir, name) -> name.endsWith(".json")); + Map sayingMap = new TreeMap<>(new Comparator() { + @Override + public int compare(Long o1, Long o2) { + return o2.compareTo(o1); + } + }); + + if (files != null) { + for (File file : files) { + ObjectMapper objectMapper = new ObjectMapper(); + + List> rawMap = objectMapper.readValue(file, List.class); + + for (Map stringObjectMap : rawMap) { + String id = stringObjectMap.get("id").toString(); + String author = (String) stringObjectMap.get("author"); + String content = (String) stringObjectMap.get("content"); + + Saying saying = new Saying(author, content); + + sayingMap.put(Long.parseLong(id), saying); + } + } + } + return sayingMap; + } + + public static Long readLastId() throws IOException { + String directoryPath = "db/wiseSaying/lastId.txt"; + File file = new File(directoryPath); + + if (!file.exists()) { + return 1L; + } + + try (BufferedReader reader = new BufferedReader(new FileReader(file))) { + String line = reader.readLine(); // 파일에서 첫 번째 줄 읽기 + if (line != null) { + return Long.parseLong(line.trim())+1; // 읽은 값을 Long으로 변환 + } else { + throw new IOException("File is empty: " + directoryPath); + } + } + } +} diff --git a/board/src/main/java/com/programmers/devcourse/view/JsonDBOutputView.java b/board/src/main/java/com/programmers/devcourse/view/JsonDBOutputView.java new file mode 100644 index 0000000..4b9560c --- /dev/null +++ b/board/src/main/java/com/programmers/devcourse/view/JsonDBOutputView.java @@ -0,0 +1,65 @@ +package com.programmers.devcourse.view; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.programmers.devcourse.model.Board; +import com.programmers.devcourse.model.Saying; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.*; + +public class JsonDBOutputView { + public static void saveSaying(Board board) throws IOException { + String folderPath = "db/wiseSaying/"; + String filePath = folderPath + "data.json"; + + File directory = new File(folderPath); + if (!directory.exists()) { + boolean dirCreated = directory.mkdirs(); + if (!dirCreated) { + throw new IOException("디렉토리 생성에 실패했습니다."); + } + } + + LinkedList> list = new LinkedList<>(); + Map elementsMap = board.getElementsMap(); + + for (Map.Entry sayingEntry : elementsMap.entrySet()) { + HashMap map = new HashMap<>(); + map.put("id", sayingEntry.getKey()); + map.put("content", sayingEntry.getValue().getContents()); + map.put("author", sayingEntry.getValue().getAuthor()); + list.addLast(map); + } + + ObjectMapper objectMapper = new ObjectMapper(); + String json = objectMapper.writeValueAsString(list); + + // 파일에 JSON 저장 (덮어쓰기 처리) + try (FileWriter fileWriter = new FileWriter(filePath, false)) { + fileWriter.write(json); + fileWriter.flush(); + } catch (IOException e) { + throw new IOException("파일 저장 중 오류 발생: " + e.getMessage()); + } + } + + public static void lastSayingNumber(long lastSayingNumber) throws IOException { + String folderPath = "db/wiseSaying/"; + String filePath = folderPath + "lastId.txt"; + ObjectMapper objectMapper = new ObjectMapper(); + + File directory = new File(folderPath); + if (!directory.exists()) { + directory.mkdirs(); // 디렉토리 생성 + } + + String json = objectMapper.writeValueAsString(lastSayingNumber); + + try (FileWriter fileWriter = new FileWriter(filePath)) { + fileWriter.write(json); + fileWriter.flush(); // 데이터 강제 저장 + } + } +} diff --git a/board/src/main/java/com/programmers/devcourse/view/OutputView.java b/board/src/main/java/com/programmers/devcourse/view/OutputView.java new file mode 100644 index 0000000..1cd395b --- /dev/null +++ b/board/src/main/java/com/programmers/devcourse/view/OutputView.java @@ -0,0 +1,49 @@ +package com.programmers.devcourse.view; + +import com.programmers.devcourse.model.Board; +import com.programmers.devcourse.model.Saying; + +import java.util.Map; + +public class OutputView { + public void printInit() { + System.out.println("== 명언 앱 =="); + } + + public void printRegisterNumber(long registerNumber) { + System.out.println(registerNumber + "번 명언이 등록되었습니다."); + } + + public void printOptions() { + System.out.println("번호 / 작가 / 명언"); + System.out.println("----------------------"); + } + + public void printSayingInfo(Board board) { + Map element = board.getElementsMap(); + for (Map.Entry entry : element.entrySet()) { + System.out.println(entry.getKey() + " " + "/" + " " + entry.getValue().getAuthor() + " " + "/" + " " + entry.getValue().getContents()); + } + } + + public void printRemove(String id) { + System.out.println(id + "번 명언이 삭제되었습니다."); + } + + public void printNotExistSayingNumber(String id) { + System.out.println(id + "번 명언은 존재하지 않습니다."); + } + + public void printOriginalSayingContent(String contents) { + System.out.println("명언(기존) : " + contents); + } + + public void printOriginalSayingAuthor(String author) { + System.out.println("작가(기존) : " + author); + + } + + public void printBuildSuccess() { + System.out.println("data.json 파일의 내용이 갱신되었습니다."); + } +} diff --git a/board/src/main/resources/application.properties b/board/src/main/resources/application.properties new file mode 100644 index 0000000..efe2ab5 --- /dev/null +++ b/board/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.application.name=board diff --git a/board/src/test/java/com/programmers/devcourse/lecture1/Lecture1ApplicationTest.java b/board/src/test/java/com/programmers/devcourse/lecture1/Lecture1ApplicationTest.java new file mode 100644 index 0000000..6f4f80d --- /dev/null +++ b/board/src/test/java/com/programmers/devcourse/lecture1/Lecture1ApplicationTest.java @@ -0,0 +1,36 @@ +package com.programmers.devcourse.lecture1; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +class Lecture1ApplicationTest { + private static ByteArrayOutputStream outputMessage; + + @BeforeEach + void init() { + outputMessage = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputMessage)); + } + + @AfterEach + void clear() { + System.setOut(System.out); + } + + @DisplayName("종료명령어 입력시 종료테스트") + @Test + void commandExitTest() { + // given + // when + System.out.println("=====Logic Start====="); + + + System.out.println("=====Logic End====="); + // then + } +} \ No newline at end of file diff --git a/board/src/test/java/com/programmers/devcourse/model/BoardTest.java b/board/src/test/java/com/programmers/devcourse/model/BoardTest.java new file mode 100644 index 0000000..180afc5 --- /dev/null +++ b/board/src/test/java/com/programmers/devcourse/model/BoardTest.java @@ -0,0 +1,107 @@ +package com.programmers.devcourse.model; + +import org.junit.jupiter.api.Test; + +import static com.programmers.devcourse.model.CommandType.*; +import static com.programmers.devcourse.model.CommandType.LIST; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class BoardTest { + @Test + void Board생성자Test() { + // given + Board board = new Board(); + + // when + System.out.println("=====Logic Start====="); + + assertThat(board.getElementsMap()).isNotNull(); + + System.out.println("=====Logic End====="); + // then + } + + @Test + void 명령정의Test() { + // given + Board board = new Board(); + + // when + System.out.println("=====Logic Start====="); + + CommandType register = board.defineCommand("등록"); + CommandType list = board.defineCommand("목록"); + CommandType exception = board.defineCommand("예외"); + + System.out.println("=====Logic End====="); + // then + assertAll(() -> assertThat(register).isEqualTo(REGISTER), + () -> assertThat(list).isEqualTo(LIST), + () -> assertThat(exception).isEqualTo(EXIT)); + } + + @Test + void 게시판속명언추가테스트() { + // given + Board board = new Board(); + + // when + System.out.println("=====Logic Start====="); + + long sayingNumber = board.add(new Saying("author", "content")); + + System.out.println("=====Logic End====="); + // then + assertAll(() -> assertThat(board.getElementsMap().size()).isEqualTo(1), + () -> assertThat(sayingNumber).isEqualTo(1)); + + } + + @Test + void 명언원소조회테스트() { + // given + Board board = new Board(); + + // when + System.out.println("=====Logic Start====="); + + System.out.println("=====Logic End====="); + // then + assertThat(board.getElementsMap()).isNotNull(); + + } + + @Test + void 게시판에_속한_명언의개수_조회() { + // given + Board board = new Board(); + board.add(new Saying("a1", "c1")); + board.add(new Saying("a2", "c2")); + // when + System.out.println("=====Logic Start====="); + + int boardSize = board.getBoardSize(); + + System.out.println("=====Logic End====="); + // then + assertThat(boardSize).isEqualTo(2); + } + + @Test + void 삭제하기() { + // given + Board board = new Board(); + board.add(new Saying("author", "content")); + board.add(new Saying("author2", "content2")); + // when + System.out.println("=====Logic Start====="); + + board.remove(1L); + + System.out.println("=====Logic End====="); + // then + assertThat(board.getBoardSize()).isEqualTo(1); + } +} \ No newline at end of file diff --git a/board/src/test/java/com/programmers/devcourse/model/SayingTest.java b/board/src/test/java/com/programmers/devcourse/model/SayingTest.java new file mode 100644 index 0000000..0a20b41 --- /dev/null +++ b/board/src/test/java/com/programmers/devcourse/model/SayingTest.java @@ -0,0 +1,34 @@ +package com.programmers.devcourse.model; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +class SayingTest { + @Nested + @DisplayName("명언객체 필드 조회 테스트") + class SayingFieldTest { + @Test + void getFieldSuccessTest() { + // given + String author = "author"; + + String contents = "contents"; + Saying saying = new Saying(author, contents); + + // when + System.out.println("=====Logic Start====="); + + assertAll(() -> assertThat(saying.getAuthor()).isEqualTo(author), + () -> assertThat(saying.getContents()).isEqualTo(contents)); + + System.out.println("=====Logic End====="); + // then + } + } + +} \ No newline at end of file