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    }