From 918d1202e1a451ec3fbfa6d748e23e66cfc6fddb Mon Sep 17 00:00:00 2001 From: viiviii Date: Fri, 6 May 2022 14:28:54 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20String=20=EC=9D=B8=EC=88=98=20?= =?UTF-8?q?=EC=9C=A0=ED=98=95=20=EC=B6=94=EA=B0=80=20=EB=B2=84=EC=A0=84=20?= =?UTF-8?q?/=20=EB=AA=A9=EB=A1=9D=2014-10?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/chapter14/Application.java | 17 ++- src/main/java/chapter14/args/Args.java | 143 ++++++++++++++++++----- 2 files changed, 124 insertions(+), 36 deletions(-) diff --git a/src/main/java/chapter14/Application.java b/src/main/java/chapter14/Application.java index e5bf584..e01e518 100644 --- a/src/main/java/chapter14/Application.java +++ b/src/main/java/chapter14/Application.java @@ -2,16 +2,23 @@ import chapter14.args.Args; +import java.text.ParseException; + class Application { public static void main(String[] args) { - Args arg = new Args("l", args); - boolean logging = arg.getBoolean('l'); - executeApplication(logging); + try { + Args arg = new Args("l,d*", args); + boolean logging = arg.getBoolean('l'); + String directory = arg.getString('d'); + executeApplication(logging, directory); + } catch (ParseException e) { + System.out.printf("Parse error: %s\n", e.getMessage()); + } } // stub - private static void executeApplication(boolean logging) { - System.out.printf("logging: %b\n", logging); + private static void executeApplication(boolean logging, String directory) { + System.out.printf("logging: %b, directory: %s\n", logging, directory); } } diff --git a/src/main/java/chapter14/args/Args.java b/src/main/java/chapter14/args/Args.java index 6f75c8e..4eec841 100644 --- a/src/main/java/chapter14/args/Args.java +++ b/src/main/java/chapter14/args/Args.java @@ -1,58 +1,85 @@ package chapter14.args; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; +import java.text.ParseException; +import java.util.*; public class Args { private String scheme; private String[] args; - private boolean valid; + private boolean valid = true; private Set unexpectedArguments = new TreeSet<>(); private Map booleanArgs = new HashMap<>(); - private int numberOfArguments = 0; + private Map stringArgs = new HashMap<>(); + private Set argsFound = new HashSet<>(); + private int currentArgument; + private char errorArgument = '\0'; - public Args(String schema, String[] args) { + enum ErrorCode { + OK, MISSING_STRING + } + + private ErrorCode errorCode = ErrorCode.OK; + + public Args(String schema, String[] args) throws ParseException { this.scheme = schema; this.args = args; valid = parse(); } - public boolean isValid() { - return valid; - } - - private boolean parse() { + private boolean parse() throws ParseException { if (scheme.length() == 0 && args.length == 0) return true; parseSchema(); parseArguments(); - return unexpectedArguments.size() == 0; + return valid; } - private boolean parseSchema() { + private boolean parseSchema() throws ParseException { for (String element : scheme.split(",")) { - parseSchemaElement(element); + if (element.length() > 0) { + String trimmedElement = element.trim(); + parseSchemaElement(trimmedElement); + } } return true; } - private void parseSchemaElement(String element) { - if (element.length() == 1) { - parseBooleanSchemeElement(element); - } + private void parseSchemaElement(String element) throws ParseException { + char elementId = element.charAt(0); + String elementTail = element.substring(1); + validateSchemaElementId(elementId); + if (isBooleanSchemaElement(elementTail)) + parseBooleanSchemaElement(elementId); + else if (isStringSchemaElement(elementTail)) + parseStringSchemaElement(elementId); } - private void parseBooleanSchemeElement(String element) { - char c = element.charAt(0); - if (Character.isLetter(c)) { - booleanArgs.put(c, false); + private void validateSchemaElementId(char elementId) throws ParseException { + if (!Character.isLetter(elementId)) { + throw new ParseException( + "Bad character: " + elementId + " in Args format: " + scheme, 0); } } + private void parseStringSchemaElement(char elementId) { + stringArgs.put(elementId, ""); + } + + private boolean isStringSchemaElement(String elementTail) { + return elementTail.equals("*"); + } + + private boolean isBooleanSchemaElement(String elementTail) { + return elementTail.length() == 0; + } + + private void parseBooleanSchemaElement(char elementId) { + booleanArgs.put(elementId, false); + } + private boolean parseArguments() { - for (String arg : args) { + for (currentArgument = 0; currentArgument < args.length; currentArgument++) { + String arg = args[currentArgument]; parseArgument(arg); } return true; @@ -70,14 +97,41 @@ private void parseElements(String arg) { } private void parseElement(char argChar) { - if (isBoolean(argChar)) { - numberOfArguments++; - setBooleanArg(argChar, true); - } else { + if (setArgument(argChar)) + argsFound.add(argChar); + else { unexpectedArguments.add(argChar); + valid = false; } } + private boolean setArgument(char argChar) { + boolean set = true; + if (isBoolean(argChar)) + setBooleanArg(argChar, true); + else if (isString(argChar)) + setStringArg(argChar, ""); + else + set = false; + + return set; + } + + private void setStringArg(char argChar, String s) { + currentArgument++; + try { + stringArgs.put(argChar, args[currentArgument]); + } catch (ArrayIndexOutOfBoundsException e) { + valid = false; + errorArgument = argChar; + errorCode = ErrorCode.MISSING_STRING; + } + } + + private boolean isString(char argChar) { + return stringArgs.containsKey(argChar); + } + private void setBooleanArg(char argChar, boolean value) { booleanArgs.put(argChar, value); } @@ -87,7 +141,7 @@ private boolean isBoolean(char argChar) { } public int cardinality() { - return numberOfArguments; + return argsFound.size(); } public String usage() { @@ -97,10 +151,17 @@ public String usage() { return ""; } - public String errorMessage() { + public String errorMessage() throws Exception { if (unexpectedArguments.size() > 0) { return unexpectedArgumentMessage(); } else { + switch (errorCode) { + case MISSING_STRING: + return String.format("Could not find string parameter for -%c.", + errorArgument); + case OK: + throw new Exception("TILT: Should not get here."); + } return ""; } } @@ -116,6 +177,26 @@ private String unexpectedArgumentMessage() { } public boolean getBoolean(char arg) { - return booleanArgs.get(arg); + return falseIfNull(booleanArgs.get(arg)); + } + + private boolean falseIfNull(Boolean b) { + return b == null ? false : b; + } + + public String getString(char arg) { + return blankIfNull(stringArgs.get(arg)); + } + + private String blankIfNull(String s) { + return s == null ? "" : s; + } + + public boolean has(char arg) { + return argsFound.contains(arg); + } + + public boolean isValid() { + return valid; } } From df997bc164a955325f16d327c511730eb0c78e28 Mon Sep 17 00:00:00 2001 From: viiviii Date: Fri, 6 May 2022 15:49:48 +0900 Subject: [PATCH 2/3] =?UTF-8?q?test:=20String=20=EC=9D=B8=EC=88=98=20?= =?UTF-8?q?=EC=9C=A0=ED=98=95=20=EC=B6=94=EA=B0=80=20=EB=B2=84=EC=A0=84=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/chapter14/ApplicationTest.java | 40 ++++-- src/test/java/chapter14/args/ArgsTest.java | 122 ++++++++++++++++--- 2 files changed, 140 insertions(+), 22 deletions(-) diff --git a/src/test/java/chapter14/ApplicationTest.java b/src/test/java/chapter14/ApplicationTest.java index 101aeb8..680ed1c 100644 --- a/src/test/java/chapter14/ApplicationTest.java +++ b/src/test/java/chapter14/ApplicationTest.java @@ -7,27 +7,51 @@ class ApplicationTest extends MainMethodTest { @Test - void validBooleanArgs() { + void booleanArguments() { //given - String arg = "-l"; + String[] args = new String[]{"-l"}; //when - runMain(arg); + runMain(args); //then - assertThat(output()).contains("logging: true"); + assertThat(output()).contains("true"); } @Test - void invalidBooleanArgs() { + void stringArguments() { //given - String notStartWithHyphen = "l"; + String[] args = new String[]{"-d", "root"}; //when - runMain(notStartWithHyphen); + runMain(args); //then - assertThat(output()).contains("logging: false"); + assertThat(output()).contains("root"); + } + + @Test + void allArgumentsPresent() { + //given + String[] args = new String[]{"-l", "-d", "user"}; + + //when + runMain(args); + + //then + assertThat(output()).isEqualTo("logging: true, directory: user"); + } + + @Test + void noArguments() { + //given + String[] args = new String[0]; + + //when + runMain(args); + + //then + assertThat(output()).isEqualTo("logging: false, directory:"); } @Override diff --git a/src/test/java/chapter14/args/ArgsTest.java b/src/test/java/chapter14/args/ArgsTest.java index f1a2e4c..e0b9534 100644 --- a/src/test/java/chapter14/args/ArgsTest.java +++ b/src/test/java/chapter14/args/ArgsTest.java @@ -3,13 +3,16 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import java.text.ParseException; + import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchException; class ArgsTest { @DisplayName("scheme와 arguments가 없는 경우") @Test - void noSchemaAndArguments() { + void noSchemaAndArguments() throws Exception { //given String schema = ""; String[] arguments = new String[0]; @@ -24,7 +27,7 @@ void noSchemaAndArguments() { @DisplayName("scheme가 없고 argument가 1개 있는 경우") @Test - void noSchemaButWithOneArgument() { + void noSchemaButWithOneArgument() throws Exception { //given String schema = ""; String[] arguments = new String[]{"-x"}; @@ -40,7 +43,7 @@ void noSchemaButWithOneArgument() { @DisplayName("scheme가 없고 argument가 여러 개 있는 경우") @Test - void noSchemaButWithMultipleArguments() { + void noSchemaButWithMultipleArguments() throws Exception { //given String schema = ""; String[] arguments = new String[]{"-x", "-y"}; @@ -56,7 +59,7 @@ void noSchemaButWithMultipleArguments() { @DisplayName("boolean 값이 없는 경우") @Test - void simpleBooleanNotPresent() { + void simpleBooleanNotPresent() throws Exception { //given String schema = "x"; String[] arguments = new String[]{}; @@ -72,7 +75,7 @@ void simpleBooleanNotPresent() { @DisplayName("boolean 값이 있는 경우") @Test - void simpleBooleanPresent() { + void simpleBooleanPresent() throws Exception { //given String schema = "x"; String[] arguments = new String[]{"-x"}; @@ -88,9 +91,9 @@ void simpleBooleanPresent() { @DisplayName("boolean 값이 여러 개 있는 경우") @Test - void simpleBooleanMultiplePresent() { + void simpleBooleanMultiplePresent() throws Exception { //given - String schema = "x,y"; + String schema = "x,y,z"; String[] arguments = new String[]{"-x", "-y"}; //when @@ -99,16 +102,37 @@ void simpleBooleanMultiplePresent() { //then assertThat(args.isValid()).isTrue(); assertThat(args.cardinality()).isEqualTo(2); + assertThat(args.has('x')).isTrue(); + assertThat(args.has('y')).isTrue(); + assertThat(args.has('z')).isFalse(); assertThat(args.getBoolean('x')).isTrue(); assertThat(args.getBoolean('y')).isTrue(); + assertThat(args.getBoolean('z')).isFalse(); } - @DisplayName("boolean 값이 여러 개 중 하나만 있는 경우") + @DisplayName("String 값이 없는 경우") @Test - void simpleBooleanOnlyOnePresent() { + void simpleStringNotPresent() throws Exception { //given - String schema = "x,y,z"; - String[] arguments = new String[]{"-z"}; + String schema = "x*"; + String[] arguments = new String[]{}; + + //when + Args args = new Args(schema, arguments); + + //then + assertThat(args.isValid()).isTrue(); + assertThat(args.cardinality()).isZero(); + assertThat(args.has('x')).isFalse(); + assertThat(args.getString('x')).isEmpty(); + } + + @DisplayName("String 값이 있는 경우") + @Test + void simpleStringPresent() throws Exception { + //given + String schema = "x*"; + String[] arguments = new String[]{"-x", "param"}; //when Args args = new Args(schema, arguments); @@ -116,8 +140,78 @@ void simpleBooleanOnlyOnePresent() { //then assertThat(args.isValid()).isTrue(); assertThat(args.cardinality()).isOne(); - assertThat(args.getBoolean('x')).isFalse(); - assertThat(args.getBoolean('y')).isFalse(); - assertThat(args.getBoolean('z')).isTrue(); + assertThat(args.has('x')).isTrue(); + assertThat(args.getString('x')).isEqualTo("param"); + } + + @DisplayName("String 값이 여러 개 있는 경우") + @Test + void simpleStringMultiplePresent() throws Exception { + //given + String schema = "x*,y*,z*"; + String[] arguments = new String[]{"-x", "param1", "-y", "param2"}; + + //when + Args args = new Args(schema, arguments); + + //then + assertThat(args.isValid()).isTrue(); + assertThat(args.cardinality()).isEqualTo(2); + assertThat(args.has('x')).isTrue(); + assertThat(args.has('y')).isTrue(); + assertThat(args.has('z')).isFalse(); + assertThat(args.getString('x')).isEqualTo("param1"); + assertThat(args.getString('y')).isEqualTo("param2"); + assertThat(args.getString('z')).isEmpty(); + } + + @DisplayName("String Argument 값이 없는 경우") + @Test + void missingStringArgument() throws Exception { + //given + String schema = "x*"; + String[] arguments = new String[]{"-x"}; // missing + + //when + Args args = new Args(schema, arguments); + + //then + assertThat(args.isValid()).isFalse(); + assertThat(args.cardinality()).isOne(); + assertThat(args.has('x')).isTrue(); + assertThat(args.getString('x')).isEmpty(); + assertThat(args.errorMessage()).isEqualTo("Could not find string parameter for -x."); + } + + @DisplayName("Schema의 ElementId가 문자가 아니면 ParseException이 발생한다") + @Test + void throwParseExceptionWhenInvalidSchemaElementId() { + //given + String containsNonLetterElementId = "l,d*,#"; + String[] arguments = new String[0]; + + //when + Exception exception = catchException(() -> new Args(containsNonLetterElementId, arguments)); + + //then + assertThat(exception) + .isInstanceOf(ParseException.class) + .hasMessage("Bad character: # in Args format: l,d*,#"); + } + + @DisplayName("유효한 args에서 errorMessage를 조회할 경우 Excpetion이 발생한다") + @Test + void errorMessageThrowExceptionWhenErrorCodeIsOK() throws Exception { + //given + Args args = new Args("x", new String[]{"-x"}); + assertThat(args.isValid()).isTrue(); + + //when + Exception exception = catchException(args::errorMessage); + + //then + assertThat(exception) + .isInstanceOf(Exception.class) + .hasMessage("TILT: Should not get here."); } } \ No newline at end of file From f45c3c8598eb898862d9e72fc880c366a56e2750 Mon Sep 17 00:00:00 2001 From: viiviii Date: Fri, 6 May 2022 15:50:39 +0900 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20schema=20=EC=98=A4=ED=83=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/chapter14/args/Args.java | 14 +++++++------- src/test/java/chapter14/args/ArgsTest.java | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/chapter14/args/Args.java b/src/main/java/chapter14/args/Args.java index 4eec841..add22cc 100644 --- a/src/main/java/chapter14/args/Args.java +++ b/src/main/java/chapter14/args/Args.java @@ -4,7 +4,7 @@ import java.util.*; public class Args { - private String scheme; + private String schema; private String[] args; private boolean valid = true; private Set unexpectedArguments = new TreeSet<>(); @@ -21,13 +21,13 @@ enum ErrorCode { private ErrorCode errorCode = ErrorCode.OK; public Args(String schema, String[] args) throws ParseException { - this.scheme = schema; + this.schema = schema; this.args = args; valid = parse(); } private boolean parse() throws ParseException { - if (scheme.length() == 0 && args.length == 0) + if (schema.length() == 0 && args.length == 0) return true; parseSchema(); parseArguments(); @@ -35,7 +35,7 @@ private boolean parse() throws ParseException { } private boolean parseSchema() throws ParseException { - for (String element : scheme.split(",")) { + for (String element : schema.split(",")) { if (element.length() > 0) { String trimmedElement = element.trim(); parseSchemaElement(trimmedElement); @@ -57,7 +57,7 @@ else if (isStringSchemaElement(elementTail)) private void validateSchemaElementId(char elementId) throws ParseException { if (!Character.isLetter(elementId)) { throw new ParseException( - "Bad character: " + elementId + " in Args format: " + scheme, 0); + "Bad character: " + elementId + " in Args format: " + schema, 0); } } @@ -145,8 +145,8 @@ public int cardinality() { } public String usage() { - if (scheme.length() > 0) - return "-[" + scheme + "]"; + if (schema.length() > 0) + return "-[" + schema + "]"; else return ""; } diff --git a/src/test/java/chapter14/args/ArgsTest.java b/src/test/java/chapter14/args/ArgsTest.java index e0b9534..d62504b 100644 --- a/src/test/java/chapter14/args/ArgsTest.java +++ b/src/test/java/chapter14/args/ArgsTest.java @@ -10,7 +10,7 @@ class ArgsTest { - @DisplayName("scheme와 arguments가 없는 경우") + @DisplayName("schema와 arguments가 없는 경우") @Test void noSchemaAndArguments() throws Exception { //given @@ -25,7 +25,7 @@ void noSchemaAndArguments() throws Exception { assertThat(args.cardinality()).isZero(); } - @DisplayName("scheme가 없고 argument가 1개 있는 경우") + @DisplayName("schema가 없고 argument가 1개 있는 경우") @Test void noSchemaButWithOneArgument() throws Exception { //given @@ -41,7 +41,7 @@ void noSchemaButWithOneArgument() throws Exception { assertThat(args.errorMessage()).isEqualTo("Argument(s) -x unexpected."); } - @DisplayName("scheme가 없고 argument가 여러 개 있는 경우") + @DisplayName("schema가 없고 argument가 여러 개 있는 경우") @Test void noSchemaButWithMultipleArguments() throws Exception { //given