001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 package org.apache.oozie.cli; 019 020 import org.apache.commons.cli.Options; 021 import org.apache.commons.cli.GnuParser; 022 import org.apache.commons.cli.ParseException; 023 import org.apache.commons.cli.CommandLine; 024 import org.apache.commons.cli.HelpFormatter; 025 026 import java.util.Map; 027 import java.util.LinkedHashMap; 028 import java.text.MessageFormat; 029 import java.io.PrintWriter; 030 import java.util.HashSet; 031 import java.util.Set; 032 033 /** 034 * Command line parser based on Apache common-cli 1.x that supports subcommands. 035 */ 036 public class CLIParser { 037 private static final String LEFT_PADDING = " "; 038 039 private String cliName; 040 private String[] cliHelp; 041 private Map<String, Options> commands = new LinkedHashMap<String, Options>(); 042 private Map<String, Boolean> commandWithArgs = new LinkedHashMap<String, Boolean>(); 043 private Map<String, String> commandsHelp = new LinkedHashMap<String, String>(); 044 045 /** 046 * Create a parser. 047 * 048 * @param cliName name of the parser, for help purposes. 049 * @param cliHelp help for the CLI. 050 */ 051 public CLIParser(String cliName, String[] cliHelp) { 052 this.cliName = cliName; 053 this.cliHelp = cliHelp; 054 } 055 056 /** 057 * Add a command to the parser. 058 * 059 * @param command comand name. 060 * @param argsHelp command arguments help. 061 * @param commandHelp command description. 062 * @param commandOptions command options. 063 * @param hasArguments 064 */ 065 public void addCommand(String command, String argsHelp, String commandHelp, Options commandOptions, 066 boolean hasArguments) { 067 String helpMsg = argsHelp + ((hasArguments) ? "<ARGS> " : "") + ": " + commandHelp; 068 commandsHelp.put(command, helpMsg); 069 commands.put(command, commandOptions); 070 commandWithArgs.put(command, hasArguments); 071 } 072 073 /** 074 * Bean that represents a parsed command. 075 */ 076 public class Command { 077 private String name; 078 private CommandLine commandLine; 079 080 private Command(String name, CommandLine commandLine) { 081 this.name = name; 082 this.commandLine = commandLine; 083 } 084 085 /** 086 * Return the command name. 087 * 088 * @return the command name. 089 */ 090 public String getName() { 091 return name; 092 } 093 094 /** 095 * Return the command line. 096 * 097 * @return the command line. 098 */ 099 public CommandLine getCommandLine() { 100 return commandLine; 101 } 102 } 103 104 /** 105 * Parse a array of arguments into a command. 106 * 107 * @param args array of arguments. 108 * @return the parsed Command. 109 * @throws ParseException thrown if the arguments could not be parsed. 110 */ 111 public Command parse(String[] args) throws ParseException { 112 if (args.length == 0) { 113 throw new ParseException("missing sub-command"); 114 } 115 else { 116 if (commands.containsKey(args[0])) { 117 GnuParser parser = new GnuParser(); 118 String[] minusCommand = new String[args.length - 1]; 119 System.arraycopy(args, 1, minusCommand, 0, minusCommand.length); 120 return new Command(args[0], parser.parse(commands.get(args[0]), minusCommand, 121 commandWithArgs.get(args[0]))); 122 } 123 else { 124 throw new ParseException(MessageFormat.format("invalid sub-command [{0}]", args[0])); 125 } 126 } 127 } 128 129 public String shortHelp() { 130 return "use 'help [sub-command]' for help details"; 131 } 132 133 /** 134 * Print the help for the parser to standard output. 135 * 136 * @param commandLine the command line 137 */ 138 public void showHelp(CommandLine commandLine) { 139 PrintWriter pw = new PrintWriter(System.out); 140 pw.println("usage: "); 141 for (String s : cliHelp) { 142 pw.println(LEFT_PADDING + s); 143 } 144 pw.println(); 145 HelpFormatter formatter = new HelpFormatter(); 146 Set<String> commandsToPrint = commands.keySet(); 147 String[] args = commandLine.getArgs(); 148 if (args.length > 0 && commandsToPrint.contains(args[0])) { 149 commandsToPrint = new HashSet<String>(); 150 commandsToPrint.add(args[0]); 151 } 152 for (String comm : commandsToPrint) { 153 Options opts = commands.get(comm); 154 String s = LEFT_PADDING + cliName + " " + comm + " "; 155 if (opts.getOptions().size() > 0) { 156 pw.println(s + "<OPTIONS> " + commandsHelp.get(comm)); 157 formatter.printOptions(pw, 100, opts, s.length(), 3); 158 } 159 else { 160 pw.println(s + commandsHelp.get(comm)); 161 } 162 pw.println(); 163 } 164 pw.flush(); 165 } 166 167 } 168