001 /* 002 * Copyright (C) 2010 eXo Platform SAS. 003 * 004 * This is free software; you can redistribute it and/or modify it 005 * under the terms of the GNU Lesser General Public License as 006 * published by the Free Software Foundation; either version 2.1 of 007 * the License, or (at your option) any later version. 008 * 009 * This software is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * You should have received a copy of the GNU Lesser General Public 015 * License along with this software; if not, write to the Free 016 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 017 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 018 */ 019 020 package org.crsh.cmdline; 021 022 import org.crsh.cmdline.binding.TypeBinding; 023 024 import java.io.IOException; 025 import java.util.ArrayList; 026 import java.util.Collection; 027 import java.util.Collections; 028 import java.util.HashSet; 029 import java.util.LinkedHashMap; 030 import java.util.List; 031 import java.util.ListIterator; 032 import java.util.Map; 033 import java.util.Set; 034 035 /** 036 * Describes a command. 037 * 038 * @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> 039 * @version $Revision$ 040 */ 041 public abstract class CommandDescriptor<T, B extends TypeBinding> { 042 043 /** . */ 044 private final String name; 045 046 /** . */ 047 private final Description description; 048 049 /** . */ 050 private final Map<String, OptionDescriptor<B>> optionMap; 051 052 /** . */ 053 private final Set<String> shortOptionNames; 054 055 /** . */ 056 private final Set<String> longOptionNames; 057 058 /** . */ 059 private boolean listArgument; 060 061 /** . */ 062 private final List<OptionDescriptor<B>> options; 063 064 /** . */ 065 private final List<ArgumentDescriptor<B>> arguments; 066 067 /** . */ 068 private final List<ParameterDescriptor<B>> parameters; 069 070 /** . */ 071 private final Map<String, OptionDescriptor<B>> uOptionMap; 072 073 /** . */ 074 private final Set<String> uShortOptionNames; 075 076 /** . */ 077 private final Set<String> uLongOptionNames; 078 079 /** . */ 080 private final List<OptionDescriptor<B>> uOptions; 081 082 /** . */ 083 private final List<ArgumentDescriptor<B>> uArguments; 084 085 /** . */ 086 private final List<ParameterDescriptor<B>> uParameters; 087 088 CommandDescriptor(String name, Description description) throws IntrospectionException { 089 090 // 091 this.description = description; 092 this.optionMap = new LinkedHashMap<String, OptionDescriptor<B>>(); 093 this.arguments = new ArrayList<ArgumentDescriptor<B>>(); 094 this.options = new ArrayList<OptionDescriptor<B>>(); 095 this.name = name; 096 this.parameters = new ArrayList<ParameterDescriptor<B>>(); 097 this.listArgument = false; 098 this.shortOptionNames = new HashSet<String>(); 099 this.longOptionNames = new HashSet<String>(); 100 101 // 102 this.uOptionMap = Collections.unmodifiableMap(optionMap); 103 this.uParameters = Collections.unmodifiableList(parameters); 104 this.uOptions = Collections.unmodifiableList(options); 105 this.uArguments = Collections.unmodifiableList(arguments); 106 this.uShortOptionNames = shortOptionNames; 107 this.uLongOptionNames = longOptionNames; 108 } 109 110 /** 111 * Add a parameter to the command. 112 * 113 * @param parameter the parameter to add 114 * @throws IntrospectionException any introspection exception that would prevent the parameter to be added 115 * @throws NullPointerException if the parameter is null 116 * @throws IllegalArgumentException if the parameter is already associated with another command 117 */ 118 void addParameter(ParameterDescriptor<B> parameter) throws IntrospectionException, NullPointerException, IllegalArgumentException { 119 120 // 121 if (parameter == null) { 122 throw new NullPointerException("No null parameter accepted"); 123 } 124 if (parameter.owner != null) { 125 throw new IllegalArgumentException("The parameter is already associated with a command"); 126 } 127 128 // 129 if (parameter instanceof OptionDescriptor) { 130 OptionDescriptor<B> option = (OptionDescriptor<B>)parameter; 131 for (String optionName : option.getNames()) { 132 String name; 133 if (optionName.length() == 1) { 134 name = "-" + optionName; 135 shortOptionNames.add(name); 136 } else { 137 name = "--" + optionName; 138 longOptionNames.add(name); 139 } 140 optionMap.put(name, option); 141 } 142 options.add(option); 143 ListIterator<ParameterDescriptor<B>> i = parameters.listIterator(); 144 while (i.hasNext()) { 145 ParameterDescriptor<B> next = i.next(); 146 if (next instanceof ArgumentDescriptor<?>) { 147 i.previous(); 148 break; 149 } 150 } 151 i.add(parameter); 152 parameter.owner = this; 153 } else if (parameter instanceof ArgumentDescriptor) { 154 ArgumentDescriptor<B> argument = (ArgumentDescriptor<B>)parameter; 155 if (argument.getMultiplicity() == Multiplicity.MULTI) { 156 if (listArgument) { 157 throw new IntrospectionException(); 158 } 159 listArgument = true; 160 } 161 arguments.add(argument); 162 parameters.add(argument); 163 parameter.owner = this; 164 } 165 } 166 167 public abstract Class<T> getType(); 168 169 public abstract void printUsage(Appendable writer) throws IOException; 170 171 public abstract void printMan(Appendable writer) throws IOException; 172 173 /** 174 * Returns the command subordinates as a map. 175 * 176 * @return the subordinates 177 */ 178 public abstract Map<String, ? extends CommandDescriptor<T, ?>> getSubordinates(); 179 180 /** 181 * Returns the command parameters, the returned collection contains the command options and 182 * the command arguments. 183 * 184 * @return the command parameters 185 */ 186 public final Collection<ParameterDescriptor<B>> getParameters() { 187 return uParameters; 188 } 189 190 /** 191 * Returns the command option names. 192 * 193 * @return the command option names 194 */ 195 public final Set<String> getOptionNames() { 196 return uOptionMap.keySet(); 197 } 198 199 /** 200 * Returns the command short option names. 201 * 202 * @return the command long option names 203 */ 204 public final Set<String> getShortOptionNames() { 205 return uShortOptionNames; 206 } 207 208 /** 209 * Returns the command long option names. 210 * 211 * @return the command long option names 212 */ 213 public final Set<String> getLongOptionNames() { 214 return uLongOptionNames; 215 } 216 217 /** 218 * Returns the command options. 219 * 220 * @return the command options 221 */ 222 public final Collection<OptionDescriptor<B>> getOptions() { 223 return uOptions; 224 } 225 226 /** 227 * Returns a command option by its name. 228 * 229 * @param name the option name 230 * @return the option 231 */ 232 public final OptionDescriptor<B> getOption(String name) { 233 return optionMap.get(name); 234 } 235 236 /** 237 * Find an command option by its name. 238 * 239 * @param name the option name 240 * @return the option 241 */ 242 public abstract OptionDescriptor<?> findOption(String name); 243 244 /** 245 * Returns a list of the command arguments. 246 * 247 * @return the command arguments 248 */ 249 public final List<ArgumentDescriptor<B>> getArguments() { 250 return uArguments; 251 } 252 253 /** 254 * Returns a a specified argument by its index. 255 * 256 * @param index the argument index 257 * @return the command argument 258 * @throws IllegalArgumentException if the index is not within the bounds 259 */ 260 public final ArgumentDescriptor<B> getArgument(int index) throws IllegalArgumentException { 261 if (index < 0) { 262 throw new IllegalArgumentException(); 263 } 264 if (index >= arguments.size()) { 265 throw new IllegalArgumentException(); 266 } 267 return arguments.get(index); 268 } 269 270 /** 271 * Returns the command name. 272 * 273 * @return the command name 274 */ 275 public final String getName() { 276 return name; 277 } 278 279 /** 280 * Returns the command description. 281 * 282 * @return the command description 283 */ 284 public final Description getDescription() { 285 return description; 286 } 287 288 /** 289 * Returns the command usage, shortcut for invoking <code>getDescription().getUsage()</code> on this 290 * object. 291 * 292 * @return the command usage 293 */ 294 public final String getUsage() { 295 return description != null ? description.getUsage() : ""; 296 } 297 }