package mil.nga.giat.geowave.core.cli.parser;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.ParameterException;
import mil.nga.giat.geowave.core.cli.api.Operation;
import mil.nga.giat.geowave.core.cli.prefix.PrefixedJCommander;
import mil.nga.giat.geowave.core.cli.prefix.PrefixedJCommander.PrefixedJCommanderInitializer;
import mil.nga.giat.geowave.core.cli.spi.OperationEntry;
import mil.nga.giat.geowave.core.cli.spi.OperationRegistry;
public class OperationParser
{
private final OperationRegistry registry;
private final Set<Object> additionalObjects = new HashSet<Object>();
public OperationParser(
OperationRegistry registry ) {
this.registry = registry;
}
public OperationParser() {
this(
OperationRegistry.getInstance());
}
/**
* Parse command line arguments into the given operation. The operation will
* be prepared, and then can be directly executed, or modified before being
* executed.
*
* @param operation
* @param args
* @return
*/
public CommandLineOperationParams parse(
Operation operation,
String[] args ) {
CommandLineOperationParams params = new CommandLineOperationParams(
args);
OperationEntry topLevelEntry = registry.getOperation(operation.getClass());
// Populate the operation map.
params.getOperationMap().put(
topLevelEntry.getOperationName(),
operation);
parseInternal(
params,
topLevelEntry);
return params;
}
/**
* Search the arguments for the list of commands/operations to execute based
* on the top level operation entry given.
*
* @param topLevel
* @param args
* @return
*/
public CommandLineOperationParams parse(
Class<? extends Operation> topLevel,
String[] args ) {
CommandLineOperationParams params = new CommandLineOperationParams(
args);
OperationEntry topLevelEntry = registry.getOperation(topLevel);
parseInternal(
params,
topLevelEntry);
return params;
}
/**
* Parse, starting from the given entry.
*
* @param params
*/
private void parseInternal(
CommandLineOperationParams params,
OperationEntry topLevelEntry ) {
try {
PrefixedJCommander pluginCommander = new PrefixedJCommander();
pluginCommander.setInitializer(new OperationContext(
topLevelEntry,
params));
params.setCommander(pluginCommander);
for (Object obj : additionalObjects) {
params.getCommander().addPrefixedObject(
obj);
}
// Parse without validation so we can prepare.
params.getCommander().setAcceptUnknownOptions(
true);
params.getCommander().setValidate(
false);
params.getCommander().parse(
params.getArgs());
// Prepare stage:
for (Operation operation : params.getOperationMap().values()) {
// Do not continue
if (!operation.prepare(params)) {
params.setSuccessCode(1);
return;
}
}
// Parse with validation
PrefixedJCommander finalCommander = new PrefixedJCommander();
finalCommander.setInitializer(new OperationContext(
topLevelEntry,
params));
params.setCommander(finalCommander);
for (Object obj : additionalObjects) {
params.getCommander().addPrefixedObject(
obj);
}
params.getCommander().setAcceptUnknownOptions(
params.isAllowUnknown());
params.getCommander().setValidate(
params.isValidate());
params.getCommander().parse(
params.getArgs());
}
catch (ParameterException p) {
params.setSuccessCode(-1);
params.setSuccessMessage("Error: " + p.getMessage());
params.setSuccessException(p);
}
return;
}
/**
* Parse the command line arguments into the objects given in the
* 'additionalObjects' array. I don't really ever forsee this ever being
* used, but hey, why not.
*
* @param args
*/
public CommandLineOperationParams parse(
String[] args ) {
CommandLineOperationParams params = new CommandLineOperationParams(
args);
try {
PrefixedJCommander pluginCommander = new PrefixedJCommander();
params.setCommander(pluginCommander);
for (Object obj : additionalObjects) {
params.getCommander().addPrefixedObject(
obj);
}
params.getCommander().parse(
params.getArgs());
}
catch (ParameterException p) {
params.setSuccessCode(-1);
params.setSuccessMessage("Error: " + p.getMessage());
params.setSuccessException(p);
}
return params;
}
public Set<Object> getAdditionalObjects() {
return additionalObjects;
}
public void addAdditionalObject(
Object obj ) {
additionalObjects.add(obj);
}
public OperationRegistry getRegistry() {
return registry;
}
/**
* This class is used to lazily init child commands only when they are
* actually referenced/used by command line options. It will set itself on
* the commander, and then add its children as commands.
*/
public class OperationContext implements
PrefixedJCommanderInitializer
{
private final OperationEntry operationEntry;
private final CommandLineOperationParams params;
private Operation operation;
public OperationContext(
OperationEntry entry,
CommandLineOperationParams params ) {
this.operationEntry = entry;
this.params = params;
}
@Override
public void initialize(
PrefixedJCommander commander ) {
commander.setCaseSensitiveOptions(false);
// Add myself.
if (params.getOperationMap().containsKey(
operationEntry.getOperationName())) {
operation = params.getOperationMap().get(
operationEntry.getOperationName());
}
else {
operation = operationEntry.createInstance();
params.addOperation(
operationEntry.getOperationName(),
operation,
operationEntry.isCommand());
}
commander.addPrefixedObject(operation);
// initialize the commander by adding child operations.
for (OperationEntry child : operationEntry.getChildren()) {
commander.addCommand(
child.getOperationName(),
null);
}
// Update each command to add an initializer.
Map<String, JCommander> childCommanders = commander.getCommands();
for (OperationEntry child : operationEntry.getChildren()) {
PrefixedJCommander pCommander = (PrefixedJCommander) childCommanders.get(child.getOperationName());
pCommander.setInitializer(new OperationContext(
child,
params));
}
}
public Operation getOperation() {
return operation;
}
public OperationEntry getOperationEntry() {
return operationEntry;
}
}
}