Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions src/main/java/chapter14/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
155 changes: 118 additions & 37 deletions src/main/java/chapter14/args/Args.java
Original file line number Diff line number Diff line change
@@ -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 schema;
private String[] args;
private boolean valid;
private boolean valid = true;
private Set<Character> unexpectedArguments = new TreeSet<>();
private Map<Character, Boolean> booleanArgs = new HashMap<>();
private int numberOfArguments = 0;
private Map<Character, String> stringArgs = new HashMap<>();
private Set<Character> argsFound = new HashSet<>();
private int currentArgument;
private char errorArgument = '\0';

public Args(String schema, String[] args) {
this.scheme = schema;
this.args = args;
valid = parse();
enum ErrorCode {
OK, MISSING_STRING
}

public boolean isValid() {
return valid;
private ErrorCode errorCode = ErrorCode.OK;

public Args(String schema, String[] args) throws ParseException {
this.schema = schema;
this.args = args;
valid = parse();
}

private boolean parse() {
if (scheme.length() == 0 && args.length == 0)
private boolean parse() throws ParseException {
if (schema.length() == 0 && args.length == 0)
return true;
parseSchema();
parseArguments();
return unexpectedArguments.size() == 0;
return valid;
}

private boolean parseSchema() {
for (String element : scheme.split(",")) {
parseSchemaElement(element);
private boolean parseSchema() throws ParseException {
for (String element : schema.split(",")) {
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: " + schema, 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;
Expand All @@ -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);
}
Expand All @@ -87,20 +141,27 @@ private boolean isBoolean(char argChar) {
}

public int cardinality() {
return numberOfArguments;
return argsFound.size();
}

public String usage() {
if (scheme.length() > 0)
return "-[" + scheme + "]";
if (schema.length() > 0)
return "-[" + schema + "]";
else
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 "";
}
}
Expand All @@ -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;
}
}
40 changes: 32 additions & 8 deletions src/test/java/chapter14/ApplicationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading