use of org.springframework.roo.shell.MethodTarget in project spring-roo by spring-projects.
the class HelpServiceImpl method locateTargets.
/**
* Get the methods annotated with {@link CliCommand}
* inside the {@link CommandMarker} classes, which value attribute matches
* the given pattern.
*
* @param pattern
* @param strictMatching
* @param checkAvailabilityIndicators
* @return the @{@link CliCommand} methods that matches the given pattern
*/
private Collection<MethodTarget> locateTargets(final String pattern, final boolean strictMatching, final boolean checkAvailabilityIndicators) {
// Get all Services implement CommandMarker interface
try {
ServiceReference<?>[] references = this.context.getAllServiceReferences(CommandMarker.class.getName(), null);
for (ServiceReference<?> ref : references) {
CommandMarker command = (CommandMarker) this.context.getService(ref);
if (!commands.contains(command)) {
add(command);
}
}
} catch (InvalidSyntaxException e) {
LOGGER.warning("Cannot load CommandMarker on SimpleParser.");
}
Validate.notNull(pattern, "Buffer required");
final Collection<MethodTarget> result = new HashSet<MethodTarget>();
// is unlikely to be noticeable to a human being using the CLI)
for (final CommandMarker command : commands) {
for (final Method method : command.getClass().getMethods()) {
final CliCommand cmd = method.getAnnotation(CliCommand.class);
if (cmd != null) {
// We have a @CliCommand.
if (checkAvailabilityIndicators) {
// Decide if this @CliCommand is available at this
// moment
Boolean available = null;
for (final String value : cmd.value()) {
final MethodTarget mt = getAvailabilityIndicator(value);
if (mt != null) {
Validate.isTrue(available == null, "More than one availability indicator is defined for '" + method.toGenericString() + "'");
try {
available = (Boolean) mt.getMethod().invoke(mt.getTarget());
// We should "break" here, but we loop over
// all to ensure no conflicting availability
// indicators are defined
} catch (final Exception e) {
available = false;
}
}
}
// Skip this @CliCommand if it's not available
if (available != null && !available) {
continue;
}
}
for (final String value : cmd.value()) {
final String remainingBuffer = isMatch(pattern, value, strictMatching);
if (remainingBuffer != null) {
result.add(new MethodTarget(method, command, remainingBuffer, value));
}
}
}
}
}
return result;
}
use of org.springframework.roo.shell.MethodTarget in project spring-roo by spring-projects.
the class HelpServiceImpl method add.
public final void add(final CommandMarker command) {
synchronized (mutex) {
commands.add(command);
for (final Method method : command.getClass().getMethods()) {
final CliAvailabilityIndicator availability = method.getAnnotation(CliAvailabilityIndicator.class);
if (availability != null) {
Validate.isTrue(method.getParameterTypes().length == 0, "CliAvailabilityIndicator is only legal for 0 parameter methods ('%s')", method.toGenericString());
Validate.isTrue(method.getReturnType().equals(Boolean.TYPE), "CliAvailabilityIndicator is only legal for primitive boolean return types (%s)", method.toGenericString());
for (final String cmd : availability.value()) {
Validate.isTrue(!availabilityIndicators.containsKey(cmd), "Cannot specify an availability indicator for '%s' more than once", cmd);
availabilityIndicators.put(cmd, new MethodTarget(method, command));
}
}
}
}
}
use of org.springframework.roo.shell.MethodTarget in project spring-roo by spring-projects.
the class HelpServiceImpl method obtainHelp.
/**
* If the given pattern matches several commands, i.e. "web mvc"
* or empty, this method writes to {@link #LOGGER} the list of
* commands (command index) which names start with the given pattern.
*
* If the given pattern matches with only one command, this method
* writes to {@link #LOGGER} the full info about that command.
*
* @param pattern
*/
public void obtainHelp(String pattern) {
synchronized (mutex) {
if (pattern == null) {
pattern = "";
}
Template helpTemplate;
// Create the data-model for Freemarker engine.
Map<String, Object> fmContext = new HashMap<String, Object>();
fmContext.put("LINE_SEPARATOR", IOUtils.LINE_SEPARATOR);
fmContext.put("CMD_MAX_LENGTH", CMD_MAX_LENGTH);
fmContext.put("OPT_MAX_LENGTH", OPT_MAX_LENGTH);
// Get the methods annotated with @CliCommand that matches the pattern
final Collection<MethodTarget> matchingTargets = locateTargets(pattern, false, false);
try {
// In that case the full command help will be rendered.
if (matchingTargets.size() == 1) {
helpTemplate = new Template("cmdTemplate", new StringReader(cmdTemplateStr), new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS));
// Single command help
final MethodTarget methodTarget = matchingTargets.iterator().next();
// Argument conversion time
final Annotation[][] parameterAnnotations = methodTarget.getMethod().getParameterAnnotations();
// Offer specified help
final CliCommand cmd = methodTarget.getMethod().getAnnotation(CliCommand.class);
Validate.notNull(cmd, "CliCommand not found");
// The command has options, those method arguments annotated with @CliOption
if (parameterAnnotations.length > 0) {
// Synopsis
fmContext.put("synopsis", justify(cmd.value(), CMD_HELP_LEFT_PAD, LINE_MAX_LENGTH));
// Description
fmContext.put("description", justify(cmd.help(), CMD_HELP_LEFT_PAD, LINE_MAX_LENGTH));
// Options
Map<String, List<String>> options = new TreeMap<String, List<String>>();
fmContext.put("options", options);
// method arguments annotated with the @CliOption annotation
for (final Annotation[] annotations : parameterAnnotations) {
CliOption cliOption = null;
for (final Annotation a : annotations) {
if (a instanceof CliOption) {
cliOption = (CliOption) a;
for (final String option : cliOption.key()) {
String dashOption = "--".concat(option);
// Note justification should be done in the Freemarker template,
// but it is easier to do it here and adjust both justifications
// (cmd name and cmd help) depending on the cmd name length
String optStr = StringUtils.repeat(" ", CMD_HELP_LEFT_PAD) + (dashOption.length() <= OPT_MAX_LENGTH ? StringUtils.rightPad(dashOption, OPT_MAX_LENGTH) : dashOption);
// Add as left padding the cmd length to avoid overwrite the command on the left
// +1 to add an empty char (space) between the command and the description
options.put(optStr, justify(cliOption.help(), CMD_HELP_LEFT_PAD + OPT_MAX_LENGTH, LINE_MAX_LENGTH));
}
}
}
}
} else // The command hasn't options, usually methods without arguments
{
// Synopsis
fmContext.put("synopsis", justify(cmd.value(), CMD_HELP_LEFT_PAD, LINE_MAX_LENGTH));
// Description
fmContext.put("description", justify(cmd.help(), CMD_HELP_LEFT_PAD, LINE_MAX_LENGTH));
}
} else // There are several commands that matches the pattern. Example: "web mvc"
// In that case only a list of command names and descriptions will be rendered.
{
helpTemplate = new Template("cmdIndexTemplate", new StringReader(cmdIndexTemplateStr), new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS));
// Create the list of commands that start with the given token.
// Note that empty token will cause all commands will be rendered.
Map<String, List<String>> cmdList = new TreeMap<String, List<String>>();
// method annotation @CliCommand
for (final MethodTarget mt : matchingTargets) {
final CliCommand cmd = mt.getMethod().getAnnotation(CliCommand.class);
if (cmd != null) {
for (final String value : cmd.value()) {
// Note justification should be done in the Freemarker template,
// but it is easier to do it here and adjust both justifications
// (cmd name and cmd help) depending on the cmd name length
String cmdStr = StringUtils.repeat(" ", CMD_INDEX_LEFT_PAD) + (value.length() <= CMD_MAX_LENGTH ? StringUtils.rightPad(value, CMD_MAX_LENGTH) : value);
// Add as left padding the cmd length to avoid overwrite the command on the left
// +1 to add an empty char (space) between the command and the description
cmdList.put(cmdStr, justify(cmd.help(), CMD_INDEX_LEFT_PAD + CMD_MAX_LENGTH, LINE_MAX_LENGTH));
}
}
}
// Add the command list to the Freemarker context
fmContext.put("commands", cmdList);
}
// Merge data-model with template
Writer strWriter = new StringWriter();
helpTemplate.process(fmContext, strWriter);
LOGGER.info(strWriter.toString());
} catch (TemplateException e) {
LOGGER.log(Level.SEVERE, "Help engine internal error!", e);
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Help engine internal error!", e);
}
LOGGER.warning("** Type 'hint' (without the quotes) and hit ENTER " + "for step-by-step guidance **" + LINE_SEPARATOR);
}
}
Aggregations