diff --git a/.github/workflows/on-pr-main.yml b/.github/workflows/on-pr-main.yml
index 0d1029e..58ac411 100644
--- a/.github/workflows/on-pr-main.yml
+++ b/.github/workflows/on-pr-main.yml
@@ -4,6 +4,7 @@ on:
pull_request:
branches:
- main
+ - dev
permissions:
pull-requests: write
diff --git a/documentation/CONTRIBUTING.md b/documentation/CONTRIBUTING.md
index d5bbf86..b368e6f 100644
--- a/documentation/CONTRIBUTING.md
+++ b/documentation/CONTRIBUTING.md
@@ -12,7 +12,7 @@
풀 리퀘스트를 보내실 때, 아래의 사항들을 유의해주세요:
-- `main` 브랜치를 기준으로 하여 풀 리퀘스트를 보내주세요.
+- `dev` 브랜치를 기준으로 하여 풀 리퀘스트를 보내주세요.
- 변경사항에 대한 명확하고 간결한 설명을 포함해주세요.
- 변경된 코드에 대한 적절한 테스트가 수행되었는지 확인해주세요.
diff --git a/documentation/DOCUMENTATION.md b/documentation/DOCUMENTATION.md
index 43e2a6f..4e09840 100644
--- a/documentation/DOCUMENTATION.md
+++ b/documentation/DOCUMENTATION.md
@@ -9,7 +9,7 @@
### 생성되는 소스코드 폴더명 변경하기
-config.json의 `srcDirPrefix.value`을 수정하여, 생성되는 소스코드 폴더명의 prefix를 변경할 수 있습니다.
+`config.json`의 `srcDirPrefix.value`을 수정하여, 생성되는 소스코드 폴더명의 `prefix`를 변경할 수 있습니다.
예시로 1000번 문제에서 `srcDirPrefix.value`을 `BOJ_`로 설정하면, `BOJ_1000` 폴더가 생성됩니다. 기본 설정 값은 `p`입니다.
```json
@@ -42,7 +42,7 @@ config.json의 `srcDirPrefix.value`을 수정하여, 생성되는 소스코드
import java.util.Scanner;
/*
- BAEKJOON {{number}} {{title}}
+ BAEKJOON {{number}}번 {{title}}
https://www.acmicpc.net/problem/{{number}}
*/
@@ -94,3 +94,43 @@ public class Main {
}
}
```
+
+### 생성되는 README.md 파일의 템플릿 변경하기
+
+`config.json`의 `markdownTemplate.value`을 수정하여, 생성되는 `README.md`의 파일 내용을 변경할 수 있습니다.
+기본 설정 값은 아래와 같습니다.
+
+```markdown
+# {{title}}
+
+> 문제 번호 : {{number}}
+> 출처 : {{url}}
+
+## 문제 설명
+
+{{description}}
+```
+
+#### 예악어
+
+해당 기능에선 네가지 예약어를 지원합니다.
+
+- `{{title}}`: 문제 제목
+- `{{number}}`: 문제 번호
+- `{{url}}`: 문제 출처 URL
+- `{{description}}`: 문제 설명 *(html 태그 포함)*
+
+#### 비활성화 방법
+
+`config.json`의 `enableReadme.value`를 `"true"` 대신 `"false"`로 수정하면, README.md 파일이 생성되지 않습니다. boolean
+형태가 아닌 `"`로 감싸진 문자열로 입력해야 합니다.
+
+```json
+{
+ ...
+ "enableReadme": {
+ "value": "false"
+ },
+ ...
+}
+```
diff --git a/src/main/java/kr/huni/BojStarter.java b/src/main/java/kr/huni/BojStarter.java
index f3ef617..dd1d3d6 100644
--- a/src/main/java/kr/huni/BojStarter.java
+++ b/src/main/java/kr/huni/BojStarter.java
@@ -1,13 +1,14 @@
package kr.huni;
import java.io.IOException;
-import kr.huni.code_generator.CodeGenerator;
+import kr.huni.code_generator.FileContentGenerator;
import kr.huni.code_generator.GeneratedCode;
import kr.huni.code_runner.CodeOpenManager;
import kr.huni.file_generator.JavaSourceCodeFile;
import kr.huni.os.OperatingSystem;
import kr.huni.problem_parser.BaekjoonProblemParser;
import kr.huni.problem_parser.Problem;
+import kr.huni.user_configuration.UserConfigurationLoader;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -17,12 +18,16 @@ public class BojStarter {
private final CodeOpenManager codeOpenManager;
private final JavaSourceCodeFile fileUtil;
- private final CodeGenerator codeGenerator;
+ private final FileContentGenerator codeGenerator;
private final BaekjoonProblemParser problemParser;
public void run(final int problemNumber) {
Problem problem = problemParser.parse(problemNumber);
- GeneratedCode generatedCode = codeGenerator.generate(problem);
+ if (!problem.isExist()) {
+ log.error("문제를 찾을 수 없습니다.");
+ return;
+ }
+ GeneratedCode generatedCode = codeGenerator.generateCode(problem);
createSrcFile(problem, generatedCode);
openSourceCodeWithIde(problem);
@@ -37,7 +42,7 @@ private void openSourceCodeWithIde(Problem problem) {
IntelliJ IDEA의 idea 명령어가 설치되어 있지 않습니다.
직접 IntelliJ IDEA를 실행해서 프로젝트를 열어주세요.
생성된 프로젝트 경로 : {}
- %n
+ 상세 오류 :
""", problem.getSourceRootDirectory(), e);
}
}
@@ -45,7 +50,8 @@ private void openSourceCodeWithIde(Problem problem) {
private void createSrcFile(Problem problem, GeneratedCode generatedCode) {
try {
fileUtil.write(problem.getSourceRootDirectory(), generatedCode.mainCode(),
- generatedCode.testCode());
+ generatedCode.testCode(), codeGenerator.generateMarkdown(problem),
+ UserConfigurationLoader.getInstance().enableReadme.getValue().equals("true"));
} catch (IOException e) {
log.error("소스코드 파일 또는 디렉토리 생성에 실패했습니다.", e);
}
diff --git a/src/main/java/kr/huni/code_generator/CodeGenerator.java b/src/main/java/kr/huni/code_generator/CodeGenerator.java
deleted file mode 100644
index c7bec65..0000000
--- a/src/main/java/kr/huni/code_generator/CodeGenerator.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package kr.huni.code_generator;
-
-import kr.huni.problem_parser.Problem;
-
-public interface CodeGenerator {
-
- GeneratedCode generate(Problem problem);
-}
diff --git a/src/main/java/kr/huni/code_generator/FileContentGenerator.java b/src/main/java/kr/huni/code_generator/FileContentGenerator.java
new file mode 100644
index 0000000..e7c4a93
--- /dev/null
+++ b/src/main/java/kr/huni/code_generator/FileContentGenerator.java
@@ -0,0 +1,10 @@
+package kr.huni.code_generator;
+
+import kr.huni.problem_parser.Problem;
+
+public interface FileContentGenerator {
+
+ GeneratedCode generateCode(Problem problem);
+
+ String generateMarkdown(Problem problem);
+}
diff --git a/src/main/java/kr/huni/code_generator/SourceCodeTemplate.java b/src/main/java/kr/huni/code_generator/FileContentTemplate.java
similarity index 72%
rename from src/main/java/kr/huni/code_generator/SourceCodeTemplate.java
rename to src/main/java/kr/huni/code_generator/FileContentTemplate.java
index 569c4cb..6a2f0b5 100644
--- a/src/main/java/kr/huni/code_generator/SourceCodeTemplate.java
+++ b/src/main/java/kr/huni/code_generator/FileContentTemplate.java
@@ -4,18 +4,20 @@
import java.util.List;
import kr.huni.problem_parser.TestCase;
-public interface SourceCodeTemplate {
+public interface FileContentTemplate {
String TEST_JAVA_FILE = "code_sample/TestHelper.java";
String NO_TEST_JAVA_FILE = "code_sample/NoTestHelper.java";
String REPLACED_NUMBER = "{{number}}";
String REPLACED_TITLE = "{{title}}";
+ String REPLACED_DESCRIPTION = "{{description}}";
+ String REPLACED_URL = "{{url}}";
String REPLACED_TEST_CASES = "// {{test_case}}";
String DEFAULT_MAIN_CODE_TEMPLATE = """
import java.util.Scanner;
/*
- BAEKJOON {{number}} {{title}}
+ BAEKJOON {{number}}번 {{title}}
https://www.acmicpc.net/problem/{{number}}
*/
@@ -27,6 +29,17 @@ public static void main(String[] args) {
}
}
""";
+ String DEFAULT_MARKDOWN_TEMPLATE = """
+ # {{title}}
+
+ > 문제 번호 : {{number}} \s
+ > 출처 : {{url}}
+
+ ## 문제 설명
+
+ {{description}}
+
+ """;
/**
* Main.java 소스코드 문자를 생성하고 반환합니다.
@@ -45,4 +58,6 @@ public static void main(String[] args) {
*/
String getTestCode(List testCases) throws IOException;
+ String getMarkdownContent(int number, String title, String description);
+
}
diff --git a/src/main/java/kr/huni/code_generator/JavaCodeGenerator.java b/src/main/java/kr/huni/code_generator/JavaCodeGenerator.java
index 5075c85..a30e5b2 100644
--- a/src/main/java/kr/huni/code_generator/JavaCodeGenerator.java
+++ b/src/main/java/kr/huni/code_generator/JavaCodeGenerator.java
@@ -5,16 +5,16 @@
import lombok.extern.slf4j.Slf4j;
@Slf4j
-public class JavaCodeGenerator implements CodeGenerator {
+public class JavaCodeGenerator implements FileContentGenerator {
@Override
- public GeneratedCode generate(Problem problem) {
+ public GeneratedCode generateCode(Problem problem) {
if (problem.getTestCases() == null) {
throw new IllegalArgumentException("테스트는 null이 될 수 없습니다.");
}
try {
- SourceCodeTemplateImpl sourceCodeTemplate = new SourceCodeTemplateImpl();
+ JavaTemplate sourceCodeTemplate = new JavaTemplate();
String mainCode = sourceCodeTemplate.getMainCode(problem.getNumber(), problem.getTitle());
String testCode = sourceCodeTemplate.getTestCode(problem.getTestCases());
@@ -25,4 +25,11 @@ public GeneratedCode generate(Problem problem) {
}
}
+ @Override
+ public String generateMarkdown(Problem problem) {
+ JavaTemplate markdownTemplate = new JavaTemplate();
+ return markdownTemplate.getMarkdownContent(problem.getNumber(), problem.getTitle(),
+ problem.getDescription());
+ }
+
}
diff --git a/src/main/java/kr/huni/code_generator/SourceCodeTemplateImpl.java b/src/main/java/kr/huni/code_generator/JavaTemplate.java
similarity index 65%
rename from src/main/java/kr/huni/code_generator/SourceCodeTemplateImpl.java
rename to src/main/java/kr/huni/code_generator/JavaTemplate.java
index be10ae2..5d658fb 100644
--- a/src/main/java/kr/huni/code_generator/SourceCodeTemplateImpl.java
+++ b/src/main/java/kr/huni/code_generator/JavaTemplate.java
@@ -1,5 +1,7 @@
package kr.huni.code_generator;
+import static kr.huni.problem_parser.BaekjoonProblemParser.PROBLEM_URL;
+
import java.io.IOException;
import java.util.List;
import kr.huni.file_generator.SourceCodeFile;
@@ -7,7 +9,7 @@
import kr.huni.user_configuration.UserConfigurationField;
import kr.huni.user_configuration.UserConfigurationLoader;
-public class SourceCodeTemplateImpl implements SourceCodeTemplate {
+public class JavaTemplate implements FileContentTemplate {
public String getMainCode(int number, String title) {
String template = DEFAULT_MAIN_CODE_TEMPLATE;
@@ -47,4 +49,22 @@ public String getTestCode(List testCases) throws IOException {
String template = SourceCodeFile.readFileFromResource(TEST_JAVA_FILE);
return template.replace(REPLACED_TEST_CASES, testCaseCode.toString());
}
+
+ @Override
+ public String getMarkdownContent(int number, String title, String description) {
+ String template = DEFAULT_MARKDOWN_TEMPLATE;
+ UserConfigurationField markdownTemplate =
+ UserConfigurationLoader.getInstance().markdownTemplate;
+ boolean useCustomTemplate = markdownTemplate.getValue() != null;
+
+ if (useCustomTemplate) {
+ template = markdownTemplate.getValue();
+ }
+
+ return template
+ .replace(REPLACED_NUMBER, String.valueOf(number))
+ .replace(REPLACED_TITLE, title)
+ .replace(REPLACED_DESCRIPTION, description)
+ .replace(REPLACED_URL, PROBLEM_URL + number);
+ }
}
diff --git a/src/main/java/kr/huni/code_runner/CodeOpenCommand.java b/src/main/java/kr/huni/code_runner/CodeOpenCommand.java
deleted file mode 100644
index 722f436..0000000
--- a/src/main/java/kr/huni/code_runner/CodeOpenCommand.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package kr.huni.code_runner;
-
-public record CodeOpenCommand(
- String ideExistedCommand,
- String executeCommand
-) {
-
-}
diff --git a/src/main/java/kr/huni/code_runner/CodeOpenManager.java b/src/main/java/kr/huni/code_runner/CodeOpenManager.java
index e74aa9f..c9b97ff 100644
--- a/src/main/java/kr/huni/code_runner/CodeOpenManager.java
+++ b/src/main/java/kr/huni/code_runner/CodeOpenManager.java
@@ -10,6 +10,7 @@ public interface CodeOpenManager {
* @throws IOException 실행 실패
* @implSpec 주어진 codePath를 IDE로 열어준다
*/
- void run(String codePath, OperatingSystem operatingSystem) throws IOException;
+ void run(String codePath, OperatingSystem operatingSystem)
+ throws IOException;
}
diff --git a/src/main/java/kr/huni/code_runner/IdeaCodeOpenManager.java b/src/main/java/kr/huni/code_runner/IdeaCodeOpenManager.java
index 91a7751..ea461b0 100644
--- a/src/main/java/kr/huni/code_runner/IdeaCodeOpenManager.java
+++ b/src/main/java/kr/huni/code_runner/IdeaCodeOpenManager.java
@@ -6,14 +6,11 @@
public class IdeaCodeOpenManager implements CodeOpenManager {
public void run(String codePath, OperatingSystem os) throws IOException {
- CodeOpenCommand command = switch (os) {
- case WINDOWS -> new CodeOpenCommand("where idea", "idea");
- case LINUX, MAC -> new CodeOpenCommand("which idea", "idea");
+ String command = switch (os) {
+ case WINDOWS -> "idea.bat";
+ case LINUX, MAC -> "idea";
};
- boolean ideaExist = Runtime.getRuntime().exec(command.ideExistedCommand()).exitValue() == 0;
- if (ideaExist) {
- Runtime.getRuntime().exec(command.executeCommand() + " " + codePath);
- }
+ Runtime.getRuntime().exec(command + " " + codePath);
}
}
diff --git a/src/main/java/kr/huni/file_generator/JavaSourceCodeFile.java b/src/main/java/kr/huni/file_generator/JavaSourceCodeFile.java
index 3e92209..a270b09 100644
--- a/src/main/java/kr/huni/file_generator/JavaSourceCodeFile.java
+++ b/src/main/java/kr/huni/file_generator/JavaSourceCodeFile.java
@@ -8,7 +8,8 @@
public class JavaSourceCodeFile implements SourceCodeFile {
@Override
- public void write(String sourceRootDirectory, String sourceCode, String testCode)
+ public void write(String sourceRootDirectory, String sourceCode, String testCode, String readme,
+ boolean enableReadme)
throws IOException {
File srcDir = new File(sourceRootDirectory, "src");
@@ -18,6 +19,13 @@ public void write(String sourceRootDirectory, String sourceCode, String testCode
log.info("소스코드 디렉토리 생성 완료");
writeToFile(srcDir, "Main.java", sourceCode);
writeToFile(srcDir, "TestHelper.java", testCode);
+ log.info("소스코드 파일 생성 완료");
+
+ if (enableReadme) {
+ writeToFile(srcDir, "README.md", readme);
+ log.info("README.md 파일 생성 완료");
+ }
}
+
}
diff --git a/src/main/java/kr/huni/file_generator/SourceCodeFile.java b/src/main/java/kr/huni/file_generator/SourceCodeFile.java
index 115ac55..703c9e6 100644
--- a/src/main/java/kr/huni/file_generator/SourceCodeFile.java
+++ b/src/main/java/kr/huni/file_generator/SourceCodeFile.java
@@ -8,7 +8,7 @@
import java.io.InputStreamReader;
import java.util.Objects;
import java.util.Scanner;
-import kr.huni.code_generator.SourceCodeTemplateImpl;
+import kr.huni.code_generator.JavaTemplate;
/**
* 소스코드 파일을 생성하고, 내용을 채워주기 위한 인터페이스
@@ -25,16 +25,18 @@ public interface SourceCodeFile {
* @implSpec 해당 메서드안에서 필요한 하위 폴더를 생성하고, {@link #writeToFile(File, String, String)}를 통해 알고리즘을 구현할
* 소스코드 파일과 테스트 코드 파일을 생성해야합니다.
*/
- void write(String directory, String sourceCode, String testCode) throws IOException;
+ void write(String directory, String sourceCode, String testCode, String readme,
+ boolean enableReadme)
+ throws IOException;
/**
* 파일을 생성하고, 내용을 채웁니다.
*
- * @param srcDir 소스코드를 저장할 위치가 담긴 객체
- * @param fileName 파일 이름
- * @param sourceCode 파일 내용
+ * @param srcDir 소스코드를 저장할 위치가 담긴 객체
+ * @param fileName 파일 이름
+ * @param content 파일 내용
*/
- default void writeToFile(File srcDir, String fileName, String sourceCode) {
+ default void writeToFile(File srcDir, String fileName, String content) {
File file = new File(srcDir, fileName);
if (file.exists()) {
System.out.printf("%s/%s가 이미 존재합니다. 새롭게 덮어 씌우시겠습니까? (y, n): ", srcDir.getAbsoluteFile(),
@@ -50,7 +52,7 @@ default void writeToFile(File srcDir, String fileName, String sourceCode) {
}
try (FileWriter fileWriter = new FileWriter(file)) {
- fileWriter.write(sourceCode);
+ fileWriter.write(content);
} catch (IOException e) {
System.out.println("파일 생성 실패. 프로그램을 종료합니다.");
throw new RuntimeException(e);
@@ -66,7 +68,7 @@ default void writeToFile(File srcDir, String fileName, String sourceCode) {
*/
static String readFileFromResource(String filePath) throws IOException {
StringBuilder sourceCode = new StringBuilder();
- try (InputStream inputStream = SourceCodeTemplateImpl.class.getClassLoader()
+ try (InputStream inputStream = JavaTemplate.class.getClassLoader()
.getResourceAsStream(filePath);
InputStreamReader inputStreamReader = new InputStreamReader(
Objects.requireNonNull(inputStream));
@@ -80,5 +82,4 @@ static String readFileFromResource(String filePath) throws IOException {
}
return sourceCode.toString();
}
-
}
diff --git a/src/main/java/kr/huni/problem_parser/BaekjoonProblemParser.java b/src/main/java/kr/huni/problem_parser/BaekjoonProblemParser.java
index f0162d6..b3e75cd 100644
--- a/src/main/java/kr/huni/problem_parser/BaekjoonProblemParser.java
+++ b/src/main/java/kr/huni/problem_parser/BaekjoonProblemParser.java
@@ -7,10 +7,11 @@
*/
public class BaekjoonProblemParser {
- static final String PROBLEM_URL = "https://www.acmicpc.net/problem/";
+ public static final String PROBLEM_URL = "https://www.acmicpc.net/problem/";
static final String USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
+ "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36";
static final String PROBLEM_TITLE_SELECTOR = "span#problem_title";
+ static final String PROBLEM_DESCRIPTION_SELECTOR = "div#problem_description";
static final String PROBLEM_INPUT_SELECTOR = "pre[id^=sample-input]";
static final String PROBLEM_OUTPUT_SELECTOR = "pre[id^=sample-output]";
private final WebParser webParser;
@@ -31,18 +32,24 @@ public BaekjoonProblemParser(WebParser webParser) {
* @return Problem 객체
*/
public Problem parse(int problemNumber) {
- String title = webParser.parse(PROBLEM_TITLE_SELECTOR)[0];
+ String title = webParser.parse(PROBLEM_TITLE_SELECTOR, false)[0];
+ String description = webParser.parse(PROBLEM_DESCRIPTION_SELECTOR, true)[0];
final ArrayList testCases = new ArrayList<>();
- String[] inputs = webParser.parse(PROBLEM_INPUT_SELECTOR);
- String[] outputs = webParser.parse(PROBLEM_OUTPUT_SELECTOR);
+ String[] inputs = webParser.parse(PROBLEM_INPUT_SELECTOR, false);
+ String[] outputs = webParser.parse(PROBLEM_OUTPUT_SELECTOR, false);
assert inputs.length == outputs.length;
for (int i = 0; i < inputs.length; i++) {
testCases.add(new TestCase(inputs[i], outputs[i]));
}
- return new Problem(problemNumber, title, testCases);
+ return Problem.builder()
+ .title(title)
+ .description(description)
+ .number(problemNumber)
+ .testCases(testCases)
+ .build();
}
}
diff --git a/src/main/java/kr/huni/problem_parser/JsoupWebParser.java b/src/main/java/kr/huni/problem_parser/JsoupWebParser.java
index afd77ac..6edf1a8 100644
--- a/src/main/java/kr/huni/problem_parser/JsoupWebParser.java
+++ b/src/main/java/kr/huni/problem_parser/JsoupWebParser.java
@@ -29,12 +29,16 @@ public JsoupWebParser(int problemNumber) {
}
@Override
- public String[] parse(String selector) {
+ public String[] parse(String selector, boolean includeHtml) {
Elements result = this.document.select(selector);
String[] arr = new String[result.size()];
for (int i = 0; i < result.size(); i++) {
- arr[i] = result.get(i).text();
+ if (includeHtml) {
+ arr[i] = result.get(i).html();
+ } else {
+ arr[i] = result.get(i).text();
+ }
}
return arr;
diff --git a/src/main/java/kr/huni/problem_parser/Problem.java b/src/main/java/kr/huni/problem_parser/Problem.java
index de13896..fd72066 100644
--- a/src/main/java/kr/huni/problem_parser/Problem.java
+++ b/src/main/java/kr/huni/problem_parser/Problem.java
@@ -3,6 +3,7 @@
import java.util.List;
import kr.huni.user_configuration.UserConfiguration;
import kr.huni.user_configuration.UserConfigurationLoader;
+import lombok.Builder;
import lombok.Getter;
@Getter
@@ -10,16 +11,23 @@ public class Problem {
private final int number;
private final String title;
+ private final String description;
private final String sourceRootDirectory;
private final List testCases;
- public Problem(int number, String title, List testCases) {
+ @Builder
+ public Problem(int number, String title, String description, List testCases) {
UserConfiguration configuration = UserConfigurationLoader.getInstance();
this.number = number;
this.title = title;
this.sourceRootDirectory = configuration.srcDirPrefix.getValue() + number;
+ this.description = description;
this.testCases = testCases;
}
+ public boolean isExist() {
+ return (this.number != 0 && this.title != null);
+ }
+
}
diff --git a/src/main/java/kr/huni/problem_parser/WebParser.java b/src/main/java/kr/huni/problem_parser/WebParser.java
index 01da8fb..ad34579 100644
--- a/src/main/java/kr/huni/problem_parser/WebParser.java
+++ b/src/main/java/kr/huni/problem_parser/WebParser.java
@@ -3,10 +3,11 @@
public interface WebParser {
/**
- * @param selector 파싱할 selector
+ * @param selector 파싱할 selector
+ * @param includeHtml html 태그를 포함할지 여부
* @return 파싱된 String[]
* @implSpec 주어진 selector를 파싱하여 String[]로 반환한다
*/
- String[] parse(String selector);
+ String[] parse(String selector, boolean includeHtml);
}
diff --git a/src/main/java/kr/huni/user_configuration/UserConfiguration.java b/src/main/java/kr/huni/user_configuration/UserConfiguration.java
index 77d92be..3d331e2 100644
--- a/src/main/java/kr/huni/user_configuration/UserConfiguration.java
+++ b/src/main/java/kr/huni/user_configuration/UserConfiguration.java
@@ -1,6 +1,6 @@
package kr.huni.user_configuration;
-import kr.huni.code_generator.SourceCodeTemplateImpl;
+import kr.huni.code_generator.JavaTemplate;
import lombok.extern.slf4j.Slf4j;
/**
@@ -22,7 +22,21 @@ public class UserConfiguration {
.description("""
Main.java 파일의 템플릿입니다. 예악어 {{number}}와 {{title}}을 사용하면 문제번호와 문제제목으로 자동 치환됩니다.
""")
- .defaultValue(SourceCodeTemplateImpl.DEFAULT_MAIN_CODE_TEMPLATE)
+ .defaultValue(JavaTemplate.DEFAULT_MAIN_CODE_TEMPLATE)
+ .build();
+ public final UserConfigurationField markdownTemplate =
+ UserConfigurationField.builder()
+ .description("""
+ 문제 설명을 저장할 마크다운 파일의 템플릿입니다. 예악어 {{title}}, {{problem_number}}, {{description}}, {{source}}를 사용하면 문제제목, 문제번호, 문제설명, 출처로 자동 치환됩니다.
+ """)
+ .defaultValue(JavaTemplate.DEFAULT_MARKDOWN_TEMPLATE)
+ .build();
+ public final UserConfigurationField enableReadme =
+ UserConfigurationField.builder()
+ .description("""
+ README.md 파일을 생성할지 여부를 결정합니다.
+ """)
+ .defaultValue("true")
.build();
public static UserConfiguration defaultConfiguration() {
@@ -34,17 +48,15 @@ public static UserConfiguration defaultConfiguration() {
*/
void printValue() {
log.info("""
- 설정 정보를 출력합니다.
- srcDirPrefix : {}
- srcCommentFormat : {}
- """, srcDirPrefix.getValue(), mainCodeTemplate.getValue());
+ 설정 정보를 출력합니다.
+ srcDirPrefix : {}
+ mainCodeTemplate : {}
+ markdownTemplate : {}
+ enableReadme : {}
+ """, srcDirPrefix.getValue(), mainCodeTemplate.getValue(), markdownTemplate.getValue(),
+ enableReadme.getValue());
}
protected UserConfiguration() {
}
-
- public void merge(UserConfiguration userConfiguration) {
- this.srcDirPrefix.setValue(userConfiguration.srcDirPrefix.getValue());
- this.mainCodeTemplate.setValue(userConfiguration.mainCodeTemplate.getValue());
- }
}
diff --git a/src/main/java/kr/huni/user_configuration/UserConfigurationLoader.java b/src/main/java/kr/huni/user_configuration/UserConfigurationLoader.java
index 91c8ec2..006886a 100644
--- a/src/main/java/kr/huni/user_configuration/UserConfigurationLoader.java
+++ b/src/main/java/kr/huni/user_configuration/UserConfigurationLoader.java
@@ -37,7 +37,12 @@ private static UserConfiguration loadProperties() {
log.info("설정 파일을 읽어옵니다.");
ObjectMapper objectMapper = new ObjectMapper();
- configuration.merge(objectMapper.readValue(configFile, UserConfiguration.class));
+ // read only exist field
+ configuration = objectMapper.readerForUpdating(configuration).readValue(configFile);
+
+ // and rewrite all fields
+ String jsonString = objectMapper.writeValueAsString(configuration);
+ writeStringToFile(jsonString);
} else {
log.info("설정 파일이 존재하지 않습니다. 설정 파일을 새로 생성합니다.");
createDefaultConfigurationFile();
diff --git a/src/test/java/kr/huni/Integration/IntegrationTests.java b/src/test/java/kr/huni/Integration/IntegrationTests.java
index 0f6283f..d24d08e 100644
--- a/src/test/java/kr/huni/Integration/IntegrationTests.java
+++ b/src/test/java/kr/huni/Integration/IntegrationTests.java
@@ -20,12 +20,12 @@
class IntegrationTests {
@BeforeEach
- void setUp() throws IOException {
+ void setUp() throws IOException, NoSuchFieldException, IllegalAccessException {
TestCleaner.clean();
}
@AfterEach
- void tearDown() throws IOException {
+ void tearDown() throws IOException, NoSuchFieldException, IllegalAccessException {
TestCleaner.clean();
}
@@ -45,4 +45,21 @@ void applicationTest() {
// then
assertTrue(new File("p1000/src/Main.java").exists());
}
+
+ @Test
+ @DisplayName("프로그램에서 README.md 파일이 잘 생성된다.")
+ void Readme_generate_well() {
+ // given
+ BojStarter program = new BojStarter(
+ new FakeCodeOpen(),
+ new JavaSourceCodeFile(),
+ new JavaCodeGenerator(),
+ new BaekjoonProblemParser(new JsoupWebParser(1000)));
+
+ // when
+ program.run(1000);
+
+ // then
+ assertTrue(new File("p1000/src/README.md").exists());
+ }
}
diff --git a/src/test/java/kr/huni/TestCleaner.java b/src/test/java/kr/huni/TestCleaner.java
index 48ba9ad..9fac4d7 100644
--- a/src/test/java/kr/huni/TestCleaner.java
+++ b/src/test/java/kr/huni/TestCleaner.java
@@ -4,29 +4,41 @@
import java.io.File;
import java.io.IOException;
+import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
+import kr.huni.user_configuration.UserConfigurationLoader;
public class TestCleaner {
- static public void clean() throws IOException {
+ static public void clean() throws IOException, NoSuchFieldException, IllegalAccessException {
System.out.println("Clean up...");
// 설정 파일 삭제
- File configFile = new File(CONFIGURATION_FILE_NAME);
- if (configFile.exists()) {
- Files.deleteIfExists(Path.of(CONFIGURATION_FILE_NAME));
- assert !new File(CONFIGURATION_FILE_NAME).exists();
- }
+ clearConfigurationFile();
// 생성된 파일 삭제
Files.deleteIfExists(Path.of("p1000/src/Main.java"));
Files.deleteIfExists(Path.of("p1000/src/TestHelper.java"));
Files.deleteIfExists(Path.of("p1000/src/NoTestHelper.java"));
+ Files.deleteIfExists(Path.of("p1000/src/README.md"));
Files.deleteIfExists(Path.of("p1000/src"));
Files.deleteIfExists(Path.of("p1000"));
assert !new File("p1000").exists();
}
+ private static void clearConfigurationFile()
+ throws IOException, NoSuchFieldException, IllegalAccessException {
+ File configFile = new File(CONFIGURATION_FILE_NAME);
+ if (configFile.exists()) {
+ Files.deleteIfExists(Path.of(CONFIGURATION_FILE_NAME));
+ assert !new File(CONFIGURATION_FILE_NAME).exists();
+ }
+
+ Field instance = UserConfigurationLoader.class.getDeclaredField("config");
+ instance.setAccessible(true);
+ instance.set(null, null);
+ }
+
}
diff --git a/src/test/java/kr/huni/code_generator/CodeGeneratorTests.java b/src/test/java/kr/huni/code_generator/CodeGeneratorTests.java
index a8708c7..f7f0423 100644
--- a/src/test/java/kr/huni/code_generator/CodeGeneratorTests.java
+++ b/src/test/java/kr/huni/code_generator/CodeGeneratorTests.java
@@ -14,10 +14,11 @@ class CodeGeneratorTests {
void javaCodeGenerator_return_code() {
// given
JavaCodeGenerator javaCodeGenerator = new JavaCodeGenerator();
- Problem problem = new Problem(-1, "A+B", new ArrayList<>());
+ Problem problem = new Problem(-1, "A+B", "두 정수 A와 B를 입력받은 다음, A+B를 출력하는 프로그램을 작성하시오.",
+ new ArrayList<>());
// when
- GeneratedCode generatedCode = javaCodeGenerator.generate(problem);
+ GeneratedCode generatedCode = javaCodeGenerator.generateCode(problem);
// then
Assertions.assertAll(
diff --git a/src/test/java/kr/huni/code_generator/FakeCodeGenerator.java b/src/test/java/kr/huni/code_generator/FakeCodeGenerator.java
index e89a84e..4e5db0d 100644
--- a/src/test/java/kr/huni/code_generator/FakeCodeGenerator.java
+++ b/src/test/java/kr/huni/code_generator/FakeCodeGenerator.java
@@ -2,10 +2,17 @@
import kr.huni.problem_parser.Problem;
-public class FakeCodeGenerator implements CodeGenerator {
+public class FakeCodeGenerator implements FileContentGenerator {
@Override
- public GeneratedCode generate(Problem problem) {
+ public GeneratedCode generateCode(Problem problem) {
return new GeneratedCode("mainCode", "testCode");
}
+
+ @Override
+ public String generateMarkdown(Problem problem) {
+ return """
+ # 1. A+B
+ """;
+ }
}
diff --git a/src/test/java/kr/huni/code_generator/SourceCodeTemplateTests.java b/src/test/java/kr/huni/code_generator/SourceCodeTemplateTests.java
index 8edeb8d..bbcf2ab 100644
--- a/src/test/java/kr/huni/code_generator/SourceCodeTemplateTests.java
+++ b/src/test/java/kr/huni/code_generator/SourceCodeTemplateTests.java
@@ -16,7 +16,7 @@ class SourceCodeTemplateTests {
@DisplayName("생성되는 기본 Main.java 기본 템플릿은 문법적으로 오류가 없다.")
void main_syntax_fine() throws IOException {
// given
- SourceCodeTemplateImpl sourceCodeTemplate = new SourceCodeTemplateImpl();
+ JavaTemplate sourceCodeTemplate = new JavaTemplate();
String mainSourceCode = sourceCodeTemplate.getMainCode(1000, "A+B");
// when
@@ -33,7 +33,7 @@ void test_syntax_fine() throws IOException {
ArrayList testCases = new ArrayList<>();
testCases.add(new TestCase("1 2", "3"));
- SourceCodeTemplateImpl sourceCodeTemplate = new SourceCodeTemplateImpl();
+ JavaTemplate sourceCodeTemplate = new JavaTemplate();
String testCode = sourceCodeTemplate.getTestCode(testCases);
testCode += """
class Main {
@@ -54,7 +54,7 @@ public static void main(String[] args) {
@DisplayName("테스트 케이스가 없을때 생성된 NoTestHelper.java는 문법적으로 오류가 없다.")
void test_syntax_fine_with_no_case() throws IOException {
// given
- SourceCodeTemplateImpl sourceCodeTemplate = new SourceCodeTemplateImpl();
+ JavaTemplate sourceCodeTemplate = new JavaTemplate();
String testCode = sourceCodeTemplate.getTestCode(new ArrayList<>());
// when
@@ -68,7 +68,7 @@ void test_syntax_fine_with_no_case() throws IOException {
@DisplayName("테스트케이스가 없을때 생성된 NoTestHelper.java 는 고정된 코드 문자를 반환한다.")
void noTestHelper_load_well() throws IOException {
// given
- SourceCodeTemplateImpl sourceCodeTemplate = new SourceCodeTemplateImpl();
+ JavaTemplate sourceCodeTemplate = new JavaTemplate();
String noTestHelperCode = sourceCodeTemplate.getTestCode(new ArrayList<>());
// when & then
@@ -89,8 +89,8 @@ public static void main() {
@DisplayName("TestHelper.java 코드 템플릿에 치환 문자가 존재 한다.")
void test_replace_text_exist() throws IOException {
// given
- String codePath = SourceCodeTemplateImpl.TEST_JAVA_FILE;
- String replacedTestCaseSymbol = SourceCodeTemplateImpl.REPLACED_TEST_CASES;
+ String codePath = JavaTemplate.TEST_JAVA_FILE;
+ String replacedTestCaseSymbol = JavaTemplate.REPLACED_TEST_CASES;
// when
String sourceCode = SourceCodeFile.readFileFromResource(codePath);
diff --git a/src/test/java/kr/huni/file_generator/JavaSourceCodeFileTests.java b/src/test/java/kr/huni/file_generator/JavaSourceCodeFileTests.java
index 11412e3..e7ab5ad 100644
--- a/src/test/java/kr/huni/file_generator/JavaSourceCodeFileTests.java
+++ b/src/test/java/kr/huni/file_generator/JavaSourceCodeFileTests.java
@@ -14,17 +14,17 @@
class JavaSourceCodeFileTests {
@BeforeEach
- void setUp() throws IOException {
+ void setUp() throws IOException, NoSuchFieldException, IllegalAccessException {
TestCleaner.clean();
}
@AfterEach
- void tearDown() throws IOException {
+ void tearDown() throws IOException, NoSuchFieldException, IllegalAccessException {
TestCleaner.clean();
}
@Test
- @DisplayName("이미 존재하는 파일을 생성하려고 할때 y 입력시 파일이 덮어씌워진다.")
+ @DisplayName("이미 존재하는 소스코드 파일을 생성하려고 할때 y 입력시 파일이 덮어씌워진다.")
void writeToFile() throws IOException {
// given
String sourceRootDirectory = "p1000";
@@ -32,7 +32,7 @@ void writeToFile() throws IOException {
String overWrittenSourceCode = "over written source code";
JavaSourceCodeFile javaSourceCodeFile = new JavaSourceCodeFile();
- javaSourceCodeFile.write(sourceRootDirectory, fileName, overWrittenSourceCode);
+ javaSourceCodeFile.write(sourceRootDirectory, fileName, overWrittenSourceCode, "", false);
File srcDir = new File(sourceRootDirectory, "src");
// when
diff --git a/src/test/java/kr/huni/problem_parser/ProblemTests.java b/src/test/java/kr/huni/problem_parser/ProblemTests.java
index 5de40fb..25a0907 100644
--- a/src/test/java/kr/huni/problem_parser/ProblemTests.java
+++ b/src/test/java/kr/huni/problem_parser/ProblemTests.java
@@ -13,7 +13,8 @@ class ProblemTests {
void problem_dir_prefix_test() {
// given & when
int problemId = 1000;
- Problem problem = new Problem(problemId, "A+B", null);
+ Problem problem = new Problem(problemId, "A+B", "두 정수 A와 B를 입력받은 다음, A+B를 출력하는 프로그램을 작성하시오.",
+ null);
String srcDirPrefix = UserConfigurationLoader.getInstance().srcDirPrefix.getValue();
String expected = srcDirPrefix + problemId;
diff --git a/src/test/java/kr/huni/problem_parser/WebParserStub.java b/src/test/java/kr/huni/problem_parser/WebParserStub.java
index ae52396..5908df1 100644
--- a/src/test/java/kr/huni/problem_parser/WebParserStub.java
+++ b/src/test/java/kr/huni/problem_parser/WebParserStub.java
@@ -3,7 +3,7 @@
public class WebParserStub implements WebParser {
@Override
- public String[] parse(String selector) {
+ public String[] parse(String selector, boolean includeHtml) {
return new String[]{"String1", "String2"};
}
diff --git a/src/test/java/kr/huni/user_configuration/UserConfigurationLoaderTest.java b/src/test/java/kr/huni/user_configuration/UserConfigurationLoaderTest.java
index 32e7ec4..598fa4a 100644
--- a/src/test/java/kr/huni/user_configuration/UserConfigurationLoaderTest.java
+++ b/src/test/java/kr/huni/user_configuration/UserConfigurationLoaderTest.java
@@ -1,12 +1,28 @@
package kr.huni.user_configuration;
+import static kr.huni.user_configuration.UserConfigurationLoader.CONFIGURATION_FILE_NAME;
import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import kr.huni.TestCleaner;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
class UserConfigurationLoaderTest {
+ @BeforeEach
+ void setUp() throws IOException, NoSuchFieldException, IllegalAccessException {
+ TestCleaner.clean();
+ }
+
@Test
@DisplayName("ConfigurationLoader 인스턴스를 두 번 생성하면 같은 인스턴스를 반환한다.")
void singleton_test() {
@@ -17,4 +33,22 @@ void singleton_test() {
// when & then
assertSame(firstInstance, secondInstance);
}
+
+ @Test
+ @DisplayName("getInstance 호출시 설정 파일에 없는 필드는 기본 값으로 덮어 씌워진다.")
+ void config_overwrite_work() throws IOException {
+ // given
+ Map configMap = new HashMap<>();
+ configMap.put("srcDirPrefix", Collections.singletonMap("value", "p"));
+ ObjectMapper objectMapper = new ObjectMapper();
+ objectMapper.writeValue(new File(CONFIGURATION_FILE_NAME), configMap);
+
+ // when
+ UserConfigurationLoader.getInstance();
+
+ // then
+ File file = new File(CONFIGURATION_FILE_NAME);
+ String content = new String(Files.readAllBytes(file.toPath()));
+ assertTrue(content.contains("mainCodeTemplate"));
+ }
}
\ No newline at end of file
diff --git a/src/test/java/kr/huni/user_configuration/UserConfigurationTest.java b/src/test/java/kr/huni/user_configuration/UserConfigurationTest.java
index 8c8562f..f067b1e 100644
--- a/src/test/java/kr/huni/user_configuration/UserConfigurationTest.java
+++ b/src/test/java/kr/huni/user_configuration/UserConfigurationTest.java
@@ -15,27 +15,15 @@ void defaultConfiguration() {
UserConfiguration defaultConfiguration = UserConfiguration.defaultConfiguration();
// then
- Assertions.assertSame(defaultConfiguration.mainCodeTemplate.getDefaultValue(),
- UserConfiguration.defaultConfiguration().mainCodeTemplate.getValue());
- Assertions.assertSame(defaultConfiguration.srcDirPrefix.getDefaultValue(),
- UserConfiguration.defaultConfiguration().srcDirPrefix.getValue());
- }
-
- @Test
- @DisplayName("merge() 메서드는 인자로 받은 Configuration 객체의 value 값을 병합한다.")
- void merge() {
- // given
- UserConfiguration userConfiguration = UserConfiguration.defaultConfiguration();
- UserConfiguration userConfiguration2 = UserConfiguration.defaultConfiguration();
- userConfiguration.srcDirPrefix.setValue("1");
- userConfiguration2.srcDirPrefix.setValue("2");
-
- // when
- userConfiguration.merge(userConfiguration2);
-
- // then
- Assertions.assertSame("2", userConfiguration.srcDirPrefix.getValue());
-
+ Assertions.assertAll(
+ () -> Assertions.assertSame(defaultConfiguration.mainCodeTemplate.getDefaultValue(),
+ UserConfiguration.defaultConfiguration().mainCodeTemplate.getValue()),
+ () -> Assertions.assertSame(defaultConfiguration.srcDirPrefix.getDefaultValue(),
+ UserConfiguration.defaultConfiguration().srcDirPrefix.getValue()),
+ () -> Assertions.assertSame(defaultConfiguration.markdownTemplate.getDefaultValue(),
+ UserConfiguration.defaultConfiguration().markdownTemplate.getValue()),
+ () -> Assertions.assertSame("true", defaultConfiguration.enableReadme.getValue())
+ );
}
@Test