001 /*
002 * Copyright (C) 2012 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 public abstract class CommandDescriptor<T, B extends TypeBinding> {
036
037 /** . */
038 private final String name;
039
040 /** . */
041 private final Description description;
042
043 /** . */
044 private final Map<String, OptionDescriptor<B>> optionMap;
045
046 /** . */
047 private final Set<String> shortOptionNames;
048
049 /** . */
050 private final Set<String> longOptionNames;
051
052 /** . */
053 private boolean listArgument;
054
055 /** . */
056 private final List<OptionDescriptor<B>> options;
057
058 /** . */
059 private final List<ArgumentDescriptor<B>> arguments;
060
061 /** . */
062 private final List<ParameterDescriptor<B>> parameters;
063
064 /** . */
065 private final Map<String, OptionDescriptor<B>> uOptionMap;
066
067 /** . */
068 private final Set<String> uShortOptionNames;
069
070 /** . */
071 private final Set<String> uLongOptionNames;
072
073 /** . */
074 private final List<OptionDescriptor<B>> uOptions;
075
076 /** . */
077 private final List<ArgumentDescriptor<B>> uArguments;
078
079 /** . */
080 private final List<ParameterDescriptor<B>> uParameters;
081
082 CommandDescriptor(String name, Description description) throws IntrospectionException {
083
084 //
085 this.description = description;
086 this.optionMap = new LinkedHashMap<String, OptionDescriptor<B>>();
087 this.arguments = new ArrayList<ArgumentDescriptor<B>>();
088 this.options = new ArrayList<OptionDescriptor<B>>();
089 this.name = name;
090 this.parameters = new ArrayList<ParameterDescriptor<B>>();
091 this.listArgument = false;
092 this.shortOptionNames = new HashSet<String>();
093 this.longOptionNames = new HashSet<String>();
094
095 //
096 this.uOptionMap = Collections.unmodifiableMap(optionMap);
097 this.uParameters = Collections.unmodifiableList(parameters);
098 this.uOptions = Collections.unmodifiableList(options);
099 this.uArguments = Collections.unmodifiableList(arguments);
100 this.uShortOptionNames = shortOptionNames;
101 this.uLongOptionNames = longOptionNames;
102 }
103
104 /**
105 * Add a parameter to the command.
106 *
107 * @param parameter the parameter to add
108 * @throws IntrospectionException any introspection exception that would prevent the parameter to be added
109 * @throws NullPointerException if the parameter is null
110 * @throws IllegalArgumentException if the parameter is already associated with another command
111 */
112 void addParameter(ParameterDescriptor<B> parameter) throws IntrospectionException, NullPointerException, IllegalArgumentException {
113
114 //
115 if (parameter == null) {
116 throw new NullPointerException("No null parameter accepted");
117 }
118 if (parameter.owner != null) {
119 throw new IllegalArgumentException("The parameter is already associated with a command");
120 }
121
122 //
123 if (parameter instanceof OptionDescriptor) {
124 OptionDescriptor<B> option = (OptionDescriptor<B>)parameter;
125 for (String optionName : option.getNames()) {
126 String name;
127 if (optionName.length() == 1) {
128 name = "-" + optionName;
129 shortOptionNames.add(name);
130 } else {
131 name = "--" + optionName;
132 longOptionNames.add(name);
133 }
134 optionMap.put(name, option);
135 }
136 options.add(option);
137 ListIterator<ParameterDescriptor<B>> i = parameters.listIterator();
138 while (i.hasNext()) {
139 ParameterDescriptor<B> next = i.next();
140 if (next instanceof ArgumentDescriptor<?>) {
141 i.previous();
142 break;
143 }
144 }
145 i.add(parameter);
146 parameter.owner = this;
147 } else if (parameter instanceof ArgumentDescriptor) {
148 ArgumentDescriptor<B> argument = (ArgumentDescriptor<B>)parameter;
149 if (argument.getMultiplicity() == Multiplicity.MULTI) {
150 if (listArgument) {
151 throw new IntrospectionException();
152 }
153 listArgument = true;
154 }
155 arguments.add(argument);
156 parameters.add(argument);
157 parameter.owner = this;
158 }
159 }
160
161 public abstract Class<T> getType();
162
163 public abstract void printUsage(Appendable writer) throws IOException;
164
165 public abstract void printMan(Appendable writer) throws IOException;
166
167 /**
168 * Returns the command subordinates as a map.
169 *
170 * @return the subordinates
171 */
172 public abstract Map<String, ? extends CommandDescriptor<T, ?>> getSubordinates();
173
174 /**
175 * Returns the command parameters, the returned collection contains the command options and
176 * the command arguments.
177 *
178 * @return the command parameters
179 */
180 public final Collection<ParameterDescriptor<B>> getParameters() {
181 return uParameters;
182 }
183
184 /**
185 * Returns the command option names.
186 *
187 * @return the command option names
188 */
189 public final Set<String> getOptionNames() {
190 return uOptionMap.keySet();
191 }
192
193 /**
194 * Returns the command short option names.
195 *
196 * @return the command long option names
197 */
198 public final Set<String> getShortOptionNames() {
199 return uShortOptionNames;
200 }
201
202 /**
203 * Returns the command long option names.
204 *
205 * @return the command long option names
206 */
207 public final Set<String> getLongOptionNames() {
208 return uLongOptionNames;
209 }
210
211 /**
212 * Returns the command options.
213 *
214 * @return the command options
215 */
216 public final Collection<OptionDescriptor<B>> getOptions() {
217 return uOptions;
218 }
219
220 /**
221 * Returns a command option by its name.
222 *
223 * @param name the option name
224 * @return the option
225 */
226 public final OptionDescriptor<B> getOption(String name) {
227 return optionMap.get(name);
228 }
229
230 /**
231 * Find an command option by its name.
232 *
233 * @param name the option name
234 * @return the option
235 */
236 public abstract OptionDescriptor<?> findOption(String name);
237
238 /**
239 * Returns a list of the command arguments.
240 *
241 * @return the command arguments
242 */
243 public final List<ArgumentDescriptor<B>> getArguments() {
244 return uArguments;
245 }
246
247 /**
248 * Returns a a specified argument by its index.
249 *
250 * @param index the argument index
251 * @return the command argument
252 * @throws IllegalArgumentException if the index is not within the bounds
253 */
254 public final ArgumentDescriptor<B> getArgument(int index) throws IllegalArgumentException {
255 if (index < 0) {
256 throw new IllegalArgumentException();
257 }
258 if (index >= arguments.size()) {
259 throw new IllegalArgumentException();
260 }
261 return arguments.get(index);
262 }
263
264 /**
265 * Returns the command name.
266 *
267 * @return the command name
268 */
269 public final String getName() {
270 return name;
271 }
272
273 /**
274 * Returns the command description.
275 *
276 * @return the command description
277 */
278 public final Description getDescription() {
279 return description;
280 }
281
282 /**
283 * Returns the command usage, shortcut for invoking <code>getDescription().getUsage()</code> on this
284 * object.
285 *
286 * @return the command usage
287 */
288 public final String getUsage() {
289 return description != null ? description.getUsage() : "";
290 }
291 }