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 }