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    }