// Copyright 2014 Michel Kraemer // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package de.undercouch.citeproc.tool.shell; import java.beans.IntrospectionException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.commons.lang3.ArrayUtils; import de.undercouch.citeproc.CSLTool; import de.undercouch.underline.Command; import de.undercouch.underline.InvalidOptionException; import de.undercouch.underline.Option; import de.undercouch.underline.OptionGroup; import de.undercouch.underline.OptionIntrospector; import de.undercouch.underline.OptionIntrospector.ID; /** * Parses command lines in the interactive shell * @author Michel Kraemer */ public final class ShellCommandParser { private ShellCommandParser() { //hidden constructor } /** * Parser result */ public static class Result { private final String[] remainingArgs; private final List<Class<? extends Command>> commands; private Result(String[] remainingArgs, List<Class<? extends Command>> commands) { this.remainingArgs = remainingArgs; this.commands = commands; } /** * @return the remaining (unparsed) arguments */ public String[] getRemainingArgs() { return remainingArgs; } /** * @return a list of all parsed commands */ public List<Class<? extends Command>> getCommands() { return commands; } /** * @return the class of the first command parsed */ public Class<? extends Command> getFirstCommand() { if (commands.isEmpty()) { return null; } return commands.get(0); } /** * @return the class of the last command parsed */ public Class<? extends Command> getLastCommand() { if (commands.isEmpty()) { return null; } return commands.get(commands.size() - 1); } } /** * Splits the given command line * @param line the command line * @return an array of strings calculated by splitting the command line */ public static String[] split(String line) { return line.trim().split("\\s+"); } /** * Parses a shell command line * @param line the command line * @return the parser result * @throws IntrospectionException if a {@link de.undercouch.citeproc.tool.CSLToolCommand} * could not be introspected * @throws InvalidOptionException if the command line contains an * option (only commands are allowed in the interactive shell) */ public static Result parse(String line) throws IntrospectionException, InvalidOptionException { return parse(line, Collections.<Class<? extends Command>>emptyList()); } /** * Parses a shell command line * @param line the command line * @param excluded a collection of commands that should not be parsed * @return the parser result * @throws IntrospectionException if a {@link de.undercouch.citeproc.tool.CSLToolCommand} * could not be introspected * @throws InvalidOptionException if the command line contains an * option (only commands are allowed in the interactive shell) */ public static Result parse(String line, Collection<Class<? extends Command>> excluded) throws IntrospectionException, InvalidOptionException { String[] args = split(line); return parse(args, excluded); } /** * Parses arguments of a shell command line * @param args the arguments to parse * @param excluded a collection of commands that should not be parsed * @return the parser result * @throws IntrospectionException if a {@link de.undercouch.citeproc.tool.CSLToolCommand} * could not be introspected * @throws InvalidOptionException if the command line contains an * option (only commands are allowed in the interactive shell) */ public static Result parse(String[] args, Collection<Class<? extends Command>> excluded) throws IntrospectionException, InvalidOptionException { List<Class<? extends Command>> classes = new ArrayList<>(); return getCommandClass(args, 0, classes, new HashSet<>(excluded)); } private static Result getCommandClass(String[] args, int i, List<Class<? extends Command>> classes, Set<Class<? extends Command>> excluded) throws IntrospectionException, InvalidOptionException { if (i >= args.length) { return new Result(new String[0], classes); } if (args[i].startsWith("-")) { //block options throw new InvalidOptionException(args[i]); } OptionGroup<ID> options; if (classes.isEmpty()) { options = OptionIntrospector.introspect(CSLTool.class, AdditionalShellCommands.class); } else { options = OptionIntrospector.introspect(classes.get(classes.size() - 1)); } List<Option<ID>> commands = options.getCommands(); if (commands != null) { for (Option<ID> cmd : commands) { if (cmd.getLongName().equals(args[i])) { Class<? extends Command> cmdClass = OptionIntrospector.getCommand(cmd.getId()); if (!excluded.contains(cmdClass)) { classes.add(cmdClass); return getCommandClass(args, i + 1, classes, excluded); } } } } return new Result(ArrayUtils.subarray(args, i, args.length), classes); } }