/**
* Copyright (c) 2013-2016, The SeedStack authors <http://seedstack.org>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.seedstack.seed.core.internal.cli;
import com.google.common.base.Joiner;
import com.google.inject.MembersInjector;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.seedstack.seed.SeedException;
import org.seedstack.seed.cli.CliContext;
import org.seedstack.seed.cli.CliOption;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Guice members injector that inject logger instances.
*
* @param <T> The type of class to inject.
*/
class CliMembersInjector<T> implements MembersInjector<T> {
private final CliContext cliContext;
private final String commandName;
private final CliModel cliModel;
CliMembersInjector(CliContext cliContext, String commandName, Set<Field> fields) {
this.cliContext = cliContext;
this.commandName = commandName;
this.cliModel = new CliModel(fields);
}
@Override
public void injectMembers(T t) {
CommandLine commandLine = parseCommandLine(cliModel.getOptions(), cliContext.getArgs());
injectArgs(commandLine, t);
injectOptions(commandLine, t);
}
private void injectOptions(CommandLine commandLine, Object toInject) {
List<CliOption> optionAnnotations = cliModel.getOptionAnnotations();
List<Field> optionFields = cliModel.getOptionFields();
for (int i = 0; i < optionAnnotations.size(); i++) {
CliOption cliOption = optionAnnotations.get(i);
Field field = optionFields.get(i);
String[] value = null;
if (cliOption.valueCount() > 0 || cliOption.valueCount() == -1) {
if (commandLine.hasOption(cliOption.name())) {
value = commandLine.getOptionValues(cliOption.name());
}
if (value == null && cliOption.defaultValues().length > 0) {
value = cliOption.defaultValues();
}
if (value != null) {
if (cliOption.valueCount() != -1 && cliOption.valueCount() != value.length) {
throw SeedException.createNew(CliErrorCode.WRONG_NUMBER_OF_OPTION_ARGUMENTS)
.put("command", commandName)
.put("option", cliOption.name())
.put("given", value.length)
.put("required", cliOption.valueCount());
}
try {
Class<?> fieldType = field.getType();
field.setAccessible(true);
if (String.class.isAssignableFrom(fieldType)) {
field.set(toInject, value[0]);
} else if (fieldType.isArray() && String.class.isAssignableFrom(fieldType.getComponentType())) {
field.set(toInject, value);
} else if (Map.class.isAssignableFrom(fieldType)) {
field.set(toInject, buildOptionArgumentMap(cliOption.name(), value));
} else {
throw SeedException.createNew(CliErrorCode.UNSUPPORTED_OPTION_FIELD_TYPE)
.put("command", commandName)
.put("option", cliOption.name())
.put("fieldType", fieldType.getCanonicalName());
}
} catch (IllegalAccessException e) {
throw SeedException.wrap(e, CliErrorCode.UNABLE_TO_INJECT_OPTION)
.put("command", commandName)
.put("option", cliOption.name())
.put("field", field.getName());
}
}
} else {
try {
field.setAccessible(true);
field.set(toInject, commandLine.hasOption(cliOption.name()));
} catch (IllegalAccessException e) {
throw SeedException.wrap(e, CliErrorCode.UNABLE_TO_INJECT_OPTION)
.put("command", commandName)
.put("option", cliOption.name())
.put("field", field.getName());
}
}
}
}
private void injectArgs(CommandLine commandLine, Object object) {
Field argsField = cliModel.getArgsField();
int mandatoryArgsCount = cliModel.getMandatoryArgsCount();
if (argsField != null) {
if (commandLine.getArgs().length < mandatoryArgsCount) {
throw SeedException.createNew(CliErrorCode.MISSING_ARGUMENTS)
.put("command", commandName)
.put("required", mandatoryArgsCount)
.put("given", commandLine.getArgs().length);
} else {
argsField.setAccessible(true);
try {
argsField.set(object, commandLine.getArgs());
} catch (IllegalAccessException e) {
throw SeedException.createNew(CliErrorCode.UNABLE_TO_INJECT_ARGUMENTS)
.put("command", commandName)
.put("field", argsField.getName());
}
}
}
}
private CommandLine parseCommandLine(Options options, String[] args) {
CommandLineParser parser = new DefaultParser();
CommandLine commandLine;
try {
commandLine = parser.parse(options, args);
} catch (ParseException e) {
throw SeedException.wrap(e, CliErrorCode.ERROR_PARSING_COMMAND_LINE)
.put("command", commandName)
.put("commandLine", Joiner.on(' ').join(args));
}
return commandLine;
}
private Map<String, String> buildOptionArgumentMap(String optionName, String[] optionArguments) {
Map<String, String> optionArgumentsMap = new HashMap<>();
if (optionArguments.length % 2 == 0) {
for (int i = 0; i < optionArguments.length; i = i + 2) {
optionArgumentsMap.put(optionArguments[i], optionArguments[i + 1]);
}
} else {
throw SeedException.createNew(CliErrorCode.ODD_NUMBER_OF_OPTION_ARGUMENTS)
.put("command", commandName)
.put("option", optionName);
}
return optionArgumentsMap;
}
}