001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.util.args;
028    import org.opends.messages.Message;
029    
030    
031    
032    import java.util.ArrayList;
033    import java.util.HashMap;
034    import java.util.LinkedList;
035    
036    import static org.opends.messages.UtilityMessages.*;
037    
038    import static org.opends.server.util.StaticUtils.*;
039    
040    
041    
042    /**
043     * This class defines a data structure for holding information about a
044     * subcommand that may be used with the subcommand argument parser.  The
045     * subcommand has a name, a description, and a set of arguments.
046     */
047    public class SubCommand
048    {
049      // Indicates whether this subCommand should be hidden in the usage
050      // information.
051      private boolean isHidden;
052    
053      // The mapping between the short argument IDs and the arguments for this
054      // subcommand.
055      private HashMap<Character,Argument> shortIDMap;
056    
057      // The mapping between the long argument IDs and the arguments for this
058      // subcommand.
059      private HashMap<String,Argument> longIDMap;
060    
061      // The list of arguments associated with this subcommand.
062      private LinkedList<Argument> arguments;
063    
064      // The description for this subcommand.
065      private Message description;
066    
067      // The name of this subcommand.
068      private String name;
069    
070      // The argument parser with which this subcommand is associated.
071      private SubCommandArgumentParser parser;
072    
073      // Indicates whether this parser will allow additional unnamed
074      // arguments at the end of the list.
075      private boolean allowsTrailingArguments;
076    
077      // The maximum number of unnamed trailing arguments that may be
078      // provided.
079      private int maxTrailingArguments;
080    
081      // The minimum number of unnamed trailing arguments that may be
082      // provided.
083      private int minTrailingArguments;
084    
085      // The display name that will be used for the trailing arguments in
086      // the usage information.
087      private String trailingArgsDisplayName;
088    
089      /**
090       * Creates a new subcommand with the provided information. The
091       * subcommand will be automatically registered with the associated
092       * parser.
093       *
094       * @param parser
095       *          The argument parser with which this subcommand is
096       *          associated.
097       * @param name
098       *          The name of this subcommand.
099       * @param description
100       *          The description of this subcommand.
101       * @throws ArgumentException
102       *           If the associated argument parser already has a
103       *           subcommand with the same name.
104       */
105      public SubCommand(SubCommandArgumentParser parser, String name,
106          Message description) throws ArgumentException
107      {
108        this(parser, name, false, 0, 0, null, description);
109      }
110    
111    
112    
113      /**
114       * Creates a new subcommand with the provided information. The
115       * subcommand will be automatically registered with the associated
116       * parser.
117       *
118       * @param parser
119       *          The argument parser with which this subcommand is
120       *          associated.
121       * @param name
122       *          The name of this subcommand.
123       * @param allowsTrailingArguments
124       *          Indicates whether this parser allows unnamed trailing
125       *          arguments to be provided.
126       * @param minTrailingArguments
127       *          The minimum number of unnamed trailing arguments that
128       *          must be provided. A value less than or equal to zero
129       *          indicates that no minimum will be enforced.
130       * @param maxTrailingArguments
131       *          The maximum number of unnamed trailing arguments that
132       *          may be provided. A value less than or equal to zero
133       *          indicates that no maximum will be enforced.
134       * @param trailingArgsDisplayName
135       *          The display name that should be used as a placeholder
136       *          for unnamed trailing arguments in the generated usage
137       *          information.
138       * @param description
139       *          The description of this subcommand.
140       * @throws ArgumentException
141       *           If the associated argument parser already has a
142       *           subcommand with the same name.
143       */
144      public SubCommand(SubCommandArgumentParser parser, String name,
145          boolean allowsTrailingArguments, int minTrailingArguments,
146          int maxTrailingArguments, String trailingArgsDisplayName,
147          Message description) throws ArgumentException
148      {
149        this.parser = parser;
150        this.name = name;
151        this.description = description;
152        this.allowsTrailingArguments = allowsTrailingArguments;
153        this.minTrailingArguments = minTrailingArguments;
154        this.maxTrailingArguments = maxTrailingArguments;
155        this.trailingArgsDisplayName = trailingArgsDisplayName;
156        this.isHidden  = false;
157    
158        String nameToCheck = name;
159        if (parser.longArgumentsCaseSensitive())
160        {
161          nameToCheck = toLowerCase(name);
162        }
163    
164        if (parser.hasSubCommand(nameToCheck))
165        {
166          Message message = ERR_ARG_SUBCOMMAND_DUPLICATE_SUBCOMMAND.get(name);
167          throw new ArgumentException(message);
168        }
169    
170        parser.addSubCommand(this);
171        shortIDMap  = new HashMap<Character,Argument>();
172        longIDMap   = new HashMap<String,Argument>();
173        arguments   = new LinkedList<Argument>();
174      }
175    
176    
177    
178      /**
179       * Retrieves the name of this subcommand.
180       *
181       * @return The name of this subcommand.
182       */
183      public String getName()
184      {
185        return name;
186      }
187    
188    
189      /**
190       * Retrieves the description for this subcommand.
191       *
192       * @return  The description for this subcommand.
193       */
194      public Message getDescription()
195      {
196        return description;
197      }
198    
199    
200    
201      /**
202       * Retrieves the set of arguments for this subcommand.
203       *
204       * @return  The set of arguments for this subcommand.
205       */
206      public LinkedList<Argument> getArguments()
207      {
208        return arguments;
209      }
210    
211    
212    
213      /**
214       * Retrieves the subcommand argument with the specified short identifier.
215       *
216       * @param  shortID  The short identifier of the argument to retrieve.
217       *
218       * @return  The subcommand argument with the specified short identifier, or
219       *          <CODE>null</CODE> if there is none.
220       */
221      public Argument getArgument(Character shortID)
222      {
223        return shortIDMap.get(shortID);
224      }
225    
226    
227    
228      /**
229       * Retrieves the subcommand argument with the specified long identifier.
230       *
231       * @param  longID  The long identifier of the argument to retrieve.
232       *
233       * @return  The subcommand argument with the specified long identifier, or
234       *          <CODE>null</CODE> if there is none.
235       */
236      public Argument getArgument(String longID)
237      {
238        return longIDMap.get(longID);
239      }
240    
241    
242    
243      /**
244       * Retrieves the subcommand argument with the specified name.
245       *
246       * @param  name  The name of the argument to retrieve.
247       *
248       * @return  The subcommand argument with the specified name, or
249       *          <CODE>null</CODE> if there is no such argument.
250       */
251      public Argument getArgumentForName(String name)
252      {
253        for (Argument a : arguments)
254        {
255          if (a.getName().equals(name))
256          {
257            return a;
258          }
259        }
260    
261        return null;
262      }
263    
264    
265    
266      /**
267       * Adds the provided argument for use with this subcommand.
268       *
269       * @param  argument  The argument to add for use with this subcommand.
270       *
271       * @throws  ArgumentException  If either the short ID or long ID for the
272       *                             argument conflicts with that of another
273       *                             argument already associated with this
274       *                             subcommand.
275       */
276      public void addArgument(Argument argument)
277             throws ArgumentException
278      {
279        String argumentName = argument.getName();
280        for (Argument a : arguments)
281        {
282          if (argumentName.equals(a.getName()))
283          {
284            Message message =
285                ERR_ARG_SUBCOMMAND_DUPLICATE_ARGUMENT_NAME.get(name, argumentName);
286            throw new ArgumentException(message);
287          }
288        }
289    
290        if (parser.hasGlobalArgument(argumentName))
291        {
292          Message message =
293              ERR_ARG_SUBCOMMAND_ARGUMENT_GLOBAL_CONFLICT.get(argumentName, name);
294          throw new ArgumentException(message);
295        }
296    
297    
298        Character shortID = argument.getShortIdentifier();
299        if (shortID != null)
300        {
301          if (shortIDMap.containsKey(shortID))
302          {
303            Message message = ERR_ARG_SUBCOMMAND_DUPLICATE_SHORT_ID.
304                get(argumentName, name, String.valueOf(shortID),
305                    shortIDMap.get(shortID).getName());
306            throw new ArgumentException(message);
307          }
308    
309          Argument arg = parser.getGlobalArgumentForShortID(shortID);
310          if (arg != null)
311          {
312            Message message = ERR_ARG_SUBCOMMAND_ARGUMENT_SHORT_ID_GLOBAL_CONFLICT.
313                get(argumentName, name, String.valueOf(shortID), arg.getName());
314            throw new ArgumentException(message);
315          }
316        }
317    
318    
319        String longID = argument.getLongIdentifier();
320        if (longID != null)
321        {
322          if (! parser.longArgumentsCaseSensitive())
323          {
324            longID = toLowerCase(longID);
325          }
326    
327          if (longIDMap.containsKey(longID))
328          {
329            Message message = ERR_ARG_SUBCOMMAND_DUPLICATE_LONG_ID.get(
330                argumentName, name, argument.getLongIdentifier(),
331                longIDMap.get(longID).getName());
332            throw new ArgumentException(message);
333          }
334    
335          Argument arg = parser.getGlobalArgumentForLongID(longID);
336          if (arg != null)
337          {
338            Message message = ERR_ARG_SUBCOMMAND_ARGUMENT_LONG_ID_GLOBAL_CONFLICT.
339                get(argumentName, name, argument.getLongIdentifier(),
340                    arg.getName());
341            throw new ArgumentException(message);
342          }
343        }
344    
345    
346        arguments.add(argument);
347    
348        if (shortID != null)
349        {
350          shortIDMap.put(shortID, argument);
351        }
352    
353        if (longID != null)
354        {
355          longIDMap.put(longID, argument);
356        }
357      }
358    
359    
360    
361      /**
362       * Indicates whether this sub-command will allow unnamed trailing
363       * arguments. These will be arguments at the end of the list that
364       * are not preceded by either a long or short identifier and will
365       * need to be manually parsed by the application using this parser.
366       * Note that once an unnamed trailing argument has been identified,
367       * all remaining arguments will be classified as such.
368       *
369       * @return <CODE>true</CODE> if this sub-command allows unnamed
370       *         trailing arguments, or <CODE>false</CODE> if it does
371       *         not.
372       */
373      public boolean allowsTrailingArguments()
374      {
375        return allowsTrailingArguments;
376      }
377    
378    
379    
380      /**
381       * Retrieves the minimum number of unnamed trailing arguments that
382       * must be provided.
383       *
384       * @return The minimum number of unnamed trailing arguments that
385       *         must be provided, or a value less than or equal to zero
386       *         if no minimum will be enforced.
387       */
388      public int getMinTrailingArguments()
389      {
390        return minTrailingArguments;
391      }
392    
393    
394    
395      /**
396       * Retrieves the maximum number of unnamed trailing arguments that
397       * may be provided.
398       *
399       * @return The maximum number of unnamed trailing arguments that may
400       *         be provided, or a value less than or equal to zero if no
401       *         maximum will be enforced.
402       */
403      public int getMaxTrailingArguments()
404      {
405        return maxTrailingArguments;
406      }
407    
408    
409    
410      /**
411       * Retrieves the trailing arguments display name.
412       *
413       * @return Returns the trailing arguments display name.
414       */
415      public String getTrailingArgumentsDisplayName()
416      {
417        return trailingArgsDisplayName;
418      }
419    
420    
421    
422      /**
423       * Retrieves the set of unnamed trailing arguments that were provided on the
424       * command line.
425       *
426       * @return  The set of unnamed trailing arguments that were provided on the
427       *          command line.
428       */
429      public ArrayList<String> getTrailingArguments()
430      {
431        return parser.getTrailingArguments();
432      }
433    
434      /**
435       * Indicates whether this subcommand should be hidden from the usage
436       * information.
437       *
438       * @return <CODE>true</CODE> if this subcommand should be hidden
439       *         from the usage information, or <CODE>false</CODE> if
440       *         not.
441       */
442      public boolean isHidden()
443      {
444        return isHidden;
445      }
446    
447    
448    
449      /**
450       * Specifies whether this subcommand should be hidden from the usage
451       * information.
452       *
453       * @param isHidden
454       *          Indicates whether this subcommand should be hidden from
455       *          the usage information.
456       */
457      public void setHidden(boolean isHidden)
458      {
459        this.isHidden = isHidden;
460      }
461    }
462