Skip to content

Commit

Permalink
refactor entry retrieval and processing
Browse files Browse the repository at this point in the history
  • Loading branch information
dsikkema committed Mar 18, 2017
1 parent f82fa93 commit b19b30a
Show file tree
Hide file tree
Showing 10 changed files with 199 additions and 293 deletions.
13 changes: 0 additions & 13 deletions src/main/java/org/dsikkema/jamphony/jamphony/App.java

This file was deleted.

12 changes: 6 additions & 6 deletions src/main/java/org/dsikkema/jamphony/jamphony/CommandRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,23 +42,23 @@ public CommandRunner(
* Output, however, has no special dependency on the command and indeed we
* may want to share it with whatever program is calling this runner.
*/
public int run(String[] args) {
public int run(String[] entries) {
int exitCode = 1;
CommandInterface command;
String commandName = "";
CommandInputDefinition inputDefinition;
InputData inputData;

try {
commandName = this.getCommandName(args);
commandName = this.getCommandName(entries);
command = this.commandRegistry.getCommandInstance(commandName);
inputDefinition = this.inputDefinitionFactory.create();
command.populateInputDefinition(inputDefinition);

/**
* Note: input validation occurs inside the input data factory
*/
inputData = this.inputDataFactory.create(inputDefinition, args);
inputData = this.inputDataFactory.create(inputDefinition, entries);
exitCode = command.execute(inputData);
} catch (InputException e) {
/**
Expand Down Expand Up @@ -106,12 +106,12 @@ private String[] splitCommandString(String commandString) {
return argsWithEscapersRemoved;
}

private String getCommandName(String[] args) throws InputException {
private String getCommandName(String[] entries) throws InputException {
// check length of args
if (args.length < 1 || args[0].isEmpty()) {
if (entries.length < 1 || entries[0].isEmpty()) {
throw new InputException("No command given");
}

return args[0];
return entries[0];
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
package org.dsikkema.jamphony.jamphony.io;

public class ArgumentDefinition {
public class ArgumentDefinition extends EntryDefinition {
private String name;
private int index;
private Type type;

public ArgumentDefinition(String name, int index, Type type) {
this.name = name;
this.index = index;
this.type = type;
super(name, type);
this.index = index;
}

public String getName() {
return name;
}

public int getIndex() {
return index;
}

public Type getType() {
return type;
return this.index;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,138 +9,49 @@
* Represents the input required by the command, not actual input data itself
*/
public class CommandInputDefinition {
private final Map<String, ArgumentDefinition> arguments = new HashMap<>();
private final List< ArgumentDefinition> arguments = new ArrayList<>();
private final Map<String, OptionDefinition> options = new HashMap<>();
private final List<String> flags = new ArrayList<>();

private int argumentCount = 0;

public void addArgument(String argumentName, Type type) {
this.arguments.put(argumentName, new ArgumentDefinition(argumentName, this.argumentCount, type));
this.argumentCount++;
}

public void addOption(String optionName, Type type) {
this.options.put(optionName, new OptionDefinition(optionName, type));
}

public void addFlag(String flagName) {
this.flags.add(flagName);
}

public Map<String, ArgumentDefinition> getArguments() {
return arguments;
}

public Map<String, OptionDefinition> getOptions() {
return options;
}

public List<String> getFlags() {
return flags;
}

public int getArgumentCount() {
return argumentCount;
}

public ArgumentDefinition getArgumentDefinition(String argumentName) {
if (!this.arguments.containsKey(argumentName)) {
throw new RuntimeException("Argument '" + argumentName + "' not defined");
public ArgumentDefinition getArgumentDefinitionByIndex(int index) throws InputException {
if (index >= this.argumentCount || index < 0) {
throw new InputException("Argument at index " + index + " does not exist");
}
return this.arguments.get(argumentName);
return this.arguments.get(index);
}

public OptionDefinition getOptionDefinition(String optionName) {
public OptionDefinition getOptionDefinitionByName(String optionName) throws InputException {
if (!this.options.containsKey(optionName)) {
throw new RuntimeException("Option '" + optionName + "' not defined");
throw new InputException("Option '" + optionName + "' is not defined");
}

return this.options.get(optionName);
}

public void validate(InputData inputData) throws InputException {
// validate that no arguments are missing or extra
if (inputData.getArguments().size() > this.argumentCount) {
throw new InputException("Too many arguments given");
} else if (inputData.getArguments().size() < this.argumentCount) {
throw new InputException("Too few arguments given");
}

// validate argument types
/**
* For each defined argument, get its index and then get the given value
* at that index. Validate that value against the required type.
*/
for (ArgumentDefinition argumentDefinition : this.arguments.values()) {
String givenArgumentValue = inputData.getRawArgumentByIndex(argumentDefinition.getIndex());
if (!this.validateInputByType(givenArgumentValue, argumentDefinition.getType())) {
throw new InputException(String.format(
"Argument '%s' with value '%s' does not match expected type '%s'",
argumentDefinition.getName(),
givenArgumentValue,
argumentDefinition.getType().getName()
));
}
}

// validate no extra options
/**
* Options are inherently _optional_, therefore only validate that there
* are no extra ones
*/
for (String optionName : inputData.getOptions().keySet()) {
if (!this.options.containsKey(optionName)) {
throw new InputException(String.format("Option '%s' is not defined", optionName));
}
}

// validate option types
/**
* Validate that all given options have the correct type
*/
for (OptionDefinition optionDefinition : this.options.values()) {
String optionName = optionDefinition.getName();
String givenOptionValue = inputData.getOption(optionName);

// only validate type if option is actually provided
if (
inputData.isOptionProvided(optionName)
&& !this.validateInputByType(givenOptionValue, optionDefinition.getType())
) {
throw new InputException(String.format(
"Option '%s' with value '%s' does not match expected type '%s'",
optionDefinition.getName(),
givenOptionValue,
optionDefinition.getType().getName()
));
}
}

// validate no extra flags
for (String flag : inputData.getFlags()) {
if (!this.flags.contains(flag)) {
throw new InputException(String.format("Flag '%s' is not defined", flag));
}
}

// flags are just present or not present, no type validation required
public boolean isFlagDefined(String name) {
return this.flags.contains(name);
}

private boolean validateInputByType(String input, Type type) {
return type != Type.INT || this.isInt(input);
/**
* Called by commands to define their input
*/
public void addArgument(String argumentName, Type type) {
this.arguments.add(new ArgumentDefinition(argumentName, this.argumentCount, type));
this.argumentCount++;
}

/**
* TODO: stop abusing exception handling. Use a regex
*/
private boolean isInt(String in) {
try {
Integer.parseInt(in);
} catch (NumberFormatException e) {
return false;
}
return true;
public void addOption(String optionName, Type type) {
this.options.put(optionName, new OptionDefinition(optionName, type));
}

public void addFlag(String flagName) {
this.flags.add(flagName);
}
}

Expand Down
60 changes: 60 additions & 0 deletions src/main/java/org/dsikkema/jamphony/jamphony/io/EntryData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package org.dsikkema.jamphony.jamphony.io;

/**
* "Entry" means an argument, option, or flag. This serves as a generic way to hold entries and their values, and
* encapsulate retrieving them by type
*/
public class EntryData {

private final EntryDefinition definition;
private final Object val;

public EntryData(EntryDefinition definition, String val) throws InputException {
switch (definition.getType()) {
case INT:
if (!this.isInt(val)) {
throw new InputException(String.format(
"Entry '%s' with value '%s' does not match expected type '%s'",
definition.getName(),
val,
definition.getType().getName()
));
}
this.val = Integer.valueOf(val);
break;
case STRING:
this.val = val;
break;

default:
throw new RuntimeException("Unhandled argument type"); // just to make this compile. Should never be hit
}

this.definition = definition;
}

public String getStringValue() {
if (this.definition.getType() != Type.STRING) {
throw new RuntimeException("Argument '" + definition.getName() + "' is not of type " + Type.STRING.getName());
}

return (String)this.val;
}

public int getIntValue() {
if (this.definition.getType() != Type.INT) {
throw new RuntimeException("Argument '" + definition.getName() + "' is not of type " + Type.INT.getName());
}

return (int)this.val;
}

private boolean isInt(String in) {
try {
Integer.parseInt(in);
} catch (NumberFormatException e) {
return false;
}
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.dsikkema.jamphony.jamphony.io;

public class EntryDefinition {
private String name;
private Type type;

public EntryDefinition(String name, Type type) {
this.name = name;
this.type = type;
}

public String getName() {
return name;
}

public Type getType() {
return type;
}
}
Loading

0 comments on commit b19b30a

Please sign in to comment.