diff --git a/src/main/java/chapter14/Application.java b/src/main/java/chapter14/Application.java new file mode 100644 index 0000000..e5bf584 --- /dev/null +++ b/src/main/java/chapter14/Application.java @@ -0,0 +1,17 @@ +package chapter14; + +import chapter14.args.Args; + +class Application { + + public static void main(String[] args) { + Args arg = new Args("l", args); + boolean logging = arg.getBoolean('l'); + executeApplication(logging); + } + + // stub + private static void executeApplication(boolean logging) { + System.out.printf("logging: %b\n", logging); + } +} diff --git a/src/main/java/chapter14/args/Args.java b/src/main/java/chapter14/args/Args.java new file mode 100644 index 0000000..6f75c8e --- /dev/null +++ b/src/main/java/chapter14/args/Args.java @@ -0,0 +1,121 @@ +package chapter14.args; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +public class Args { + private String scheme; + private String[] args; + private boolean valid; + private Set unexpectedArguments = new TreeSet<>(); + private Map booleanArgs = new HashMap<>(); + private int numberOfArguments = 0; + + public Args(String schema, String[] args) { + this.scheme = schema; + this.args = args; + valid = parse(); + } + + public boolean isValid() { + return valid; + } + + private boolean parse() { + if (scheme.length() == 0 && args.length == 0) + return true; + parseSchema(); + parseArguments(); + return unexpectedArguments.size() == 0; + } + + private boolean parseSchema() { + for (String element : scheme.split(",")) { + parseSchemaElement(element); + } + return true; + } + + private void parseSchemaElement(String element) { + if (element.length() == 1) { + parseBooleanSchemeElement(element); + } + } + + private void parseBooleanSchemeElement(String element) { + char c = element.charAt(0); + if (Character.isLetter(c)) { + booleanArgs.put(c, false); + } + } + + private boolean parseArguments() { + for (String arg : args) { + parseArgument(arg); + } + return true; + } + + private void parseArgument(String arg) { + if (arg.startsWith("-")) + parseElements(arg); + } + + private void parseElements(String arg) { + for (int i = 1; i < arg.length(); i++) { + parseElement(arg.charAt(i)); + } + } + + private void parseElement(char argChar) { + if (isBoolean(argChar)) { + numberOfArguments++; + setBooleanArg(argChar, true); + } else { + unexpectedArguments.add(argChar); + } + } + + private void setBooleanArg(char argChar, boolean value) { + booleanArgs.put(argChar, value); + } + + private boolean isBoolean(char argChar) { + return booleanArgs.containsKey(argChar); + } + + public int cardinality() { + return numberOfArguments; + } + + public String usage() { + if (scheme.length() > 0) + return "-[" + scheme + "]"; + else + return ""; + } + + public String errorMessage() { + if (unexpectedArguments.size() > 0) { + return unexpectedArgumentMessage(); + } else { + return ""; + } + } + + private String unexpectedArgumentMessage() { + StringBuffer message = new StringBuffer("Argument(s) -"); + for (char c : unexpectedArguments) { + message.append(c); + } + message.append(" unexpected."); + + return message.toString(); + } + + public boolean getBoolean(char arg) { + return booleanArgs.get(arg); + } +} diff --git a/src/test/java/chapter14/ApplicationTest.java b/src/test/java/chapter14/ApplicationTest.java new file mode 100644 index 0000000..101aeb8 --- /dev/null +++ b/src/test/java/chapter14/ApplicationTest.java @@ -0,0 +1,37 @@ +package chapter14; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class ApplicationTest extends MainMethodTest { + + @Test + void validBooleanArgs() { + //given + String arg = "-l"; + + //when + runMain(arg); + + //then + assertThat(output()).contains("logging: true"); + } + + @Test + void invalidBooleanArgs() { + //given + String notStartWithHyphen = "l"; + + //when + runMain(notStartWithHyphen); + + //then + assertThat(output()).contains("logging: false"); + } + + @Override + protected void runMain(String... args) { + Application.main(args); + } +} \ No newline at end of file diff --git a/src/test/java/chapter14/MainMethodTest.java b/src/test/java/chapter14/MainMethodTest.java new file mode 100644 index 0000000..e0bf861 --- /dev/null +++ b/src/test/java/chapter14/MainMethodTest.java @@ -0,0 +1,39 @@ +package chapter14; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; + +abstract class MainMethodTest { + private final PrintStream standardOut = System.out; + private OutputStream captor; + + @BeforeEach + protected final void init() { + setSystemOutToMyPrintStreamForCaptor(); + } + + private void setSystemOutToMyPrintStreamForCaptor() { + captor = new ByteArrayOutputStream(); + System.setOut(new PrintStream(captor)); + } + + @AfterEach + protected final void printOutput() { + setSystemOutToStandard(); + System.out.println(output()); + } + + private void setSystemOutToStandard() { + System.setOut(standardOut); + } + + protected final String output() { + return captor.toString().trim(); + } + + protected abstract void runMain(String... args); +} diff --git a/src/test/java/chapter14/args/ArgsTest.java b/src/test/java/chapter14/args/ArgsTest.java new file mode 100644 index 0000000..f1a2e4c --- /dev/null +++ b/src/test/java/chapter14/args/ArgsTest.java @@ -0,0 +1,123 @@ +package chapter14.args; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class ArgsTest { + + @DisplayName("scheme와 arguments가 없는 경우") + @Test + void noSchemaAndArguments() { + //given + String schema = ""; + String[] arguments = new String[0]; + + //when + Args args = new Args(schema, arguments); + + //then + assertThat(args.isValid()).isTrue(); + assertThat(args.cardinality()).isZero(); + } + + @DisplayName("scheme가 없고 argument가 1개 있는 경우") + @Test + void noSchemaButWithOneArgument() { + //given + String schema = ""; + String[] arguments = new String[]{"-x"}; + + //when + Args args = new Args(schema, arguments); + + //then + assertThat(args.isValid()).isFalse(); + assertThat(args.cardinality()).isZero(); + assertThat(args.errorMessage()).isEqualTo("Argument(s) -x unexpected."); + } + + @DisplayName("scheme가 없고 argument가 여러 개 있는 경우") + @Test + void noSchemaButWithMultipleArguments() { + //given + String schema = ""; + String[] arguments = new String[]{"-x", "-y"}; + + //when + Args args = new Args(schema, arguments); + + //then + assertThat(args.isValid()).isFalse(); + assertThat(args.cardinality()).isZero(); + assertThat(args.errorMessage()).isEqualTo("Argument(s) -xy unexpected."); + } + + @DisplayName("boolean 값이 없는 경우") + @Test + void simpleBooleanNotPresent() { + //given + 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.getBoolean('x')).isFalse(); + } + + @DisplayName("boolean 값이 있는 경우") + @Test + void simpleBooleanPresent() { + //given + String schema = "x"; + String[] arguments = new String[]{"-x"}; + + //when + Args args = new Args(schema, arguments); + + //then + assertThat(args.isValid()).isTrue(); + assertThat(args.cardinality()).isOne(); + assertThat(args.getBoolean('x')).isTrue(); + } + + @DisplayName("boolean 값이 여러 개 있는 경우") + @Test + void simpleBooleanMultiplePresent() { + //given + String schema = "x,y"; + String[] arguments = new String[]{"-x", "-y"}; + + //when + Args args = new Args(schema, arguments); + + //then + assertThat(args.isValid()).isTrue(); + assertThat(args.cardinality()).isEqualTo(2); + assertThat(args.getBoolean('x')).isTrue(); + assertThat(args.getBoolean('y')).isTrue(); + } + + @DisplayName("boolean 값이 여러 개 중 하나만 있는 경우") + @Test + void simpleBooleanOnlyOnePresent() { + //given + String schema = "x,y,z"; + String[] arguments = new String[]{"-z"}; + + //when + Args args = new Args(schema, arguments); + + //then + assertThat(args.isValid()).isTrue(); + assertThat(args.cardinality()).isOne(); + assertThat(args.getBoolean('x')).isFalse(); + assertThat(args.getBoolean('y')).isFalse(); + assertThat(args.getBoolean('z')).isTrue(); + } +} \ No newline at end of file