package edu.uw.zookeeper.client.cli; import java.io.IOException; import java.util.List; import java.util.Map; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.AbstractIdleService; import edu.uw.zookeeper.common.Pair; import edu.uw.zookeeper.data.RootZNodePath; import edu.uw.zookeeper.data.ZNodeLabelVector; import edu.uw.zookeeper.data.ZNodePath; public class ShellInvoker extends AbstractIdleService implements Invoker<ShellInvoker.Command> { @Invokes(commands={Command.class}) public static ShellInvoker create(Shell shell) { return new ShellInvoker(shell); } public static enum Command { @CommandDescriptor(names = { "?", "help" }, description = "Print usage") HELP, @CommandDescriptor(names = { "q", "quit", "exit" }, description = "Exit program") EXIT, @CommandDescriptor(names = { "printenv" }, description = "Print environment") PRINTENV, @CommandDescriptor(names = { "cd" }, description = "Change working path", arguments = { @ArgumentDescriptor(token = TokenType.PATH)}) CD; } public static Environment.Key<String> PROMPT_KEY = Environment.Key.create("PROMPT", String.class); public static Environment.Key<ZNodeLabelVector> CWD_KEY = Environment.Key.create("CWD", ZNodeLabelVector.class); protected static final String DEFAULT_PROMPT = "%s $ "; protected static final ZNodePath DEFAULT_CWD = RootZNodePath.getInstance(); protected final Shell shell; protected ShellInvoker(Shell shell) { this.shell = shell; } @Override public void invoke(Invocation<Command> input) throws Exception { switch (input.getCommand().second()) { case HELP: help(input); break; case EXIT: exit(input); break; case PRINTENV: printEnv(input); break; case CD: cd(input); break; default: throw new IllegalArgumentException(String.valueOf(input)); } } @Override protected void startUp() throws Exception { shell.getEnvironment().put(PROMPT_KEY, DEFAULT_PROMPT); shell.getEnvironment().put(CWD_KEY, DEFAULT_CWD); for (Command command: Command.values()) { shell.getCommands().withCommand(command); } } @Override protected void shutDown() throws Exception { } protected void cd(Invocation<Command> invocation) { shell.getEnvironment().put(CWD_KEY, (ZNodeLabelVector) invocation.getArguments()[1]); } protected void printEnv(Invocation<Command> invocation) throws IOException { String output = Joiner.on('\n').withKeyValueSeparator("\t").join(shell.getEnvironment().entrySet()); shell.getReader().println(output); } protected void exit(Invocation<Command> invocation) { if (shell.isRunning()) { shell.stopAsync(); } } protected void help(Invocation<Command> invocation) throws Exception { Joiner joiner = Joiner.on('\t'); StringBuilder str = new StringBuilder(); ImmutableSet.Builder<Pair<CommandDescriptor, Object>> commands = ImmutableSet.builder(); for (Map.Entry<CharSequence, Pair<CommandDescriptor, Object>> e: shell.getCommands().getCommands()) { commands.add(e.getValue()); } for (Pair<CommandDescriptor, Object> command: commands.build()) { joiner.appendTo(str, getUsage(command)).append('\n'); } String output = str.toString(); shell.getReader().println(output); } protected List<String> getUsage(Pair<CommandDescriptor, Object> command) { ImmutableList.Builder<String> tokens = ImmutableList.builder(); tokens.add(Joiner.on(',') .appendTo(new StringBuilder().append('{'), CommandParser.getCommandNames(command)).append('}') .toString()); for (ArgumentDescriptor a : command.first().arguments()) { StringBuilder argument = new StringBuilder(); if (!a.name().isEmpty()) { argument.append(a.name()).append('='); } switch (a.token()) { case STRING: argument.append("\"...\""); break; case PATH: argument.append("/..."); break; case INTEGER: argument.append("int"); break; case ENUM: Joiner.on(',').appendTo( argument.append('{'), a.type().getEnumConstants()) .append('}').toString(); break; } if (!a.value().isEmpty()) { argument.append('(').append(a.value()).append(')'); } tokens.add(argument.toString()); } if (!command.first().description().isEmpty()) { tokens.add(command.first().description()); } return tokens.build(); } }