Regarding CLI,

"Well done" to all contributors, and thanks for CLI. Over the years I'd developed my own, uh, "technique" for doing command line options, and I always dreaded it. No more pain.

I've corrected a couple of minor flaws in HelpFormatter, and made two minor changes with regard to auto option formatting. I've sent along the modified files, feel free to use them or not as you see fit. You can find all my changes with a grep "jp:". Here's a summary of changes with (approximate) line numbers.

A) HelpFormatter was printing the 2nd and later members of option groups both in the group and as standalone options. (HelpFormatter #248 in the modified file.)

B) HelpFormatter was not printing a space after an option group. (HelpFormatter #278 in the modified file.)

X) To give predictable order of option printing, I changed a few HashMaps to LinkedHashMap at Options #91-100 and OptionGroup #76. That introduces a JVM version dependency, so you might not want to keep it. I don't know if it's worth doing the work in a 1.2-compatible way. For heaven's sake, though, please don't introduce a dependency on that horrible commons collections stuff.

Y) I added printing of argument names to the options in groups in HelpFormatter. The HelpFormatter change is at #268 in the modified version. I added an option-by-short-name accessor to OptionGroup at #97 to support that.

Yrs,
Jerry
/*
 * $Header: 
/home/cvs/jakarta-commons-sandbox/cli/src/java/org/apache/commons/cli/CommandLine.java,v
 1.4 2002/06/06 22:32:37 bayard Exp $
 * $Revision: 1.4 $
 * $Date: 2002/06/06 22:32:37 $
 *
 * ====================================================================
 *
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Commons", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact [EMAIL PROTECTED]
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 */

package org.apache.commons.cli;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

/** 
 * A formatter of help messages for the current command line options
 *
 * @author Slawek Zachcial
 * @author John Keyes (john at integralsource.com)
 **/
public class HelpFormatter
{
   // --------------------------------------------------------------- Constants

   public static final int DEFAULT_WIDTH              = 74;
   public static final int DEFAULT_LEFT_PAD           = 1;
   public static final int DEFAULT_DESC_PAD           = 3;
   public static final String DEFAULT_SYNTAX_PREFIX   = "usage: ";
   public static final String DEFAULT_OPT_PREFIX      = "-";
   public static final String DEFAULT_LONG_OPT_PREFIX = "--";
   public static final String DEFAULT_ARG_NAME        = "arg";

   // ------------------------------------------------------------------ Static

   // -------------------------------------------------------------- Attributes

   public int defaultWidth;
   public int defaultLeftPad;
   public int defaultDescPad;
   public String defaultSyntaxPrefix;
   public String defaultNewLine;
   public String defaultOptPrefix;
   public String defaultLongOptPrefix;
   public String defaultArgName;

   // ------------------------------------------------------------ Constructors
   public HelpFormatter()
   {
      defaultWidth = DEFAULT_WIDTH;
      defaultLeftPad = DEFAULT_LEFT_PAD;
      defaultDescPad = DEFAULT_DESC_PAD;
      defaultSyntaxPrefix = DEFAULT_SYNTAX_PREFIX;
      defaultNewLine = System.getProperty("line.separator");
      defaultOptPrefix = DEFAULT_OPT_PREFIX;
      defaultLongOptPrefix = DEFAULT_LONG_OPT_PREFIX;
      defaultArgName = DEFAULT_ARG_NAME;
   }

   // ------------------------------------------------------------------ Public

   public void printHelp( String cmdLineSyntax,
                          Options options )
   {
       printHelp( defaultWidth, cmdLineSyntax, null, options, null, false );
   }

   public void printHelp( String cmdLineSyntax,
                          Options options,
                          boolean autoUsage )
   {
       printHelp( defaultWidth, cmdLineSyntax, null, options, null, autoUsage );
   }

   public void printHelp( String cmdLineSyntax,
                          String header,
                          Options options,
                          String footer )
   {
       printHelp( cmdLineSyntax, header, options, footer, false );
   }

   public void printHelp( String cmdLineSyntax,
                          String header,
                          Options options,
                          String footer,
                          boolean autoUsage )
   {
      printHelp(defaultWidth, cmdLineSyntax, header, options, footer, autoUsage 
);
   }
   
   public void printHelp( int width,
                          String cmdLineSyntax,
                          String header,
                          Options options,
                          String footer )
   {
       printHelp( width, cmdLineSyntax, header, options, footer, false );
   }

   public void printHelp( int width,
                          String cmdLineSyntax,
                          String header,
                          Options options,
                          String footer,
                          boolean autoUsage )
   {
      PrintWriter pw = new PrintWriter(System.out);
      printHelp( pw, width, cmdLineSyntax, header,
                 options, defaultLeftPad, defaultDescPad, footer, autoUsage );
      pw.flush();
   }
   public void printHelp( PrintWriter pw,
                          int width,
                          String cmdLineSyntax,
                          String header,
                          Options options,
                          int leftPad,
                          int descPad,
                          String footer )
   throws IllegalArgumentException
   {
       printHelp( pw, width, cmdLineSyntax, header, options, leftPad, descPad, 
footer, false );
   }

   public void printHelp( PrintWriter pw,
                          int width,
                          String cmdLineSyntax,
                          String header,
                          Options options,
                          int leftPad,
                          int descPad,
                          String footer,
                          boolean autoUsage )
      throws IllegalArgumentException
   {
      if ( cmdLineSyntax == null || cmdLineSyntax.length() == 0 )
      {
         throw new IllegalArgumentException("cmdLineSyntax not provided");
      }

      if ( autoUsage ) {
          printUsage( pw, width, cmdLineSyntax, options );
      }
      else {
          printUsage( pw, width, cmdLineSyntax );
      }

      if ( header != null && header.trim().length() > 0 )
      {
         printWrapped( pw, width, header );
      }
      printOptions( pw, width, options, leftPad, descPad );
      if ( footer != null && footer.trim().length() > 0 )
      {
         printWrapped( pw, width, footer );
      }
   }

   /**
    * <p>Prints the usage statement for the specified application.</p>
    *
    * @param pw The PrintWriter to print the usage statement 
    * @param width ??
    * @param appName The application name
    * @param options The command line Options
    *
    */
   public void printUsage( PrintWriter pw, int width, String app, Options 
options ) 
   {
       // initialise the string buffer
       StringBuffer buff = new StringBuffer( defaultSyntaxPrefix ).append( app 
).append( " " );
       
       // create a list for processed option groups
       ArrayList list = new ArrayList();

       // temp variable
       Option option;

       // iterate over the options
       for ( Iterator i = options.getOptions().iterator(); i.hasNext(); )
       {
           // get the next Option
           option = (Option) i.next();

           // check if the option is part of an OptionGroup
           OptionGroup group = options.getOptionGroup( option );

           // if the option is part of a group and the group has not already
           // been processed
           
           /* jp: changed
           if( group != null && !list.contains(group)) {
            * to this, to keep from reprinting 2nd and later members of group */
           if( group != null ) {
               if(!list.contains( group ) ) {

                   // add the group to the processed list
                   list.add( group );

                   // get the names of the options from the OptionGroup
                   Collection names = group.getNames();

                   buff.append( "[" ); 

                   // for each option in the OptionGroup
                   for( Iterator iter = names.iterator(); iter.hasNext(); ) {
                       /* jp: added local var for option name */
                       String name = (String) iter.next();
                       buff.append( name );
                       
                       /* jp: added the arg name if the option has one */
                       Option opt = group.getOption( name );
                       if( opt.hasArgName() ) {
                           buff.append( " " ).append( opt.getArgName() );
                       }
                       
                       if( iter.hasNext() ) {
                           buff.append( " | " );
                       }
                   }
                   buff.append( "] " );
                   /* jp: changed "]" to "] " */
               }
           }
           // if the Option is not part of an OptionGroup
           else {
               // if the Option is not a required option
               if( !option.isRequired() ) {
                   buff.append( "[" );
               }
               
               if( !" ".equals( option.getOpt() ) ) {
                   buff.append( "-" ).append( option.getOpt() );
               }
               else {
                   buff.append( "--" ).append( option.getLongOpt() );
               }

               if( option.hasArg() ){
                   buff.append( " " );
               }

               // if the Option has a value
               if( option.hasArg() ) {
                   buff.append( option.getArgName() );
               }

               // if the Option is not a required option
               if( !option.isRequired() ) {
                   buff.append( "]" );
               }
               buff.append( " " );
           }
       }

       // call printWrapped
       printWrapped( pw, width, buff.toString().indexOf(' ')+1,
                     buff.toString() );
   }

   public void printUsage( PrintWriter pw, int width, String cmdLineSyntax )
   {
      int argPos = cmdLineSyntax.indexOf(' ') + 1;
      printWrapped(pw, width, defaultSyntaxPrefix.length() + argPos,
                   defaultSyntaxPrefix + cmdLineSyntax);
   }

   public void printOptions( PrintWriter pw, int width, Options options, int 
leftPad, int descPad )
   {
      StringBuffer sb = new StringBuffer();
      renderOptions(sb, width, options, leftPad, descPad);
      pw.println(sb.toString());
   }

   public void printWrapped( PrintWriter pw, int width, String text )
   {
      printWrapped(pw, width, 0, text);
   }

   public void printWrapped( PrintWriter pw, int width, int nextLineTabStop, 
String text )
   {
      StringBuffer sb = new StringBuffer(text.length());
      renderWrappedText(sb, width, nextLineTabStop, text);
      pw.println(sb.toString());
   }

   // --------------------------------------------------------------- Protected

   protected StringBuffer renderOptions( StringBuffer sb,
                                         int width,
                                         Options options,
                                         int leftPad,
                                         int descPad )
   {
      final String lpad = createPadding(leftPad);
      final String dpad = createPadding(descPad);

      //first create list containing only <lpad>-a,--aaa where -a is opt and 
--aaa is
      //long opt; in parallel look for the longest opt string
      //this list will be then used to sort options ascending
      int max = 0;
      StringBuffer optBuf;
      List prefixList = new ArrayList();
      Option option;
      List optList = options.helpOptions();
      Collections.sort( optList, new StringBufferComparator() );
      for ( Iterator i = optList.iterator(); i.hasNext(); )
      {
         option = (Option) i.next();
         optBuf = new StringBuffer(8);

         if (option.getOpt().equals(" "))
         {
             optBuf.append(lpad).append("   " + 
defaultLongOptPrefix).append(option.getLongOpt());
         }
         else
         {
             
optBuf.append(lpad).append(defaultOptPrefix).append(option.getOpt());
             if ( option.hasLongOpt() )
             {
                
optBuf.append(',').append(defaultLongOptPrefix).append(option.getLongOpt());
             }

         }

         if( option.hasArg() ) {
             if( option.hasArgName() ) {
                 optBuf.append(" <").append( option.getArgName() ).append( '>' 
);
             }
             else {
                 optBuf.append(' ');
             }
         }

         prefixList.add(optBuf);
         max = optBuf.length() > max ? optBuf.length() : max;
      }
      int x = 0;
      for ( Iterator i = optList.iterator(); i.hasNext(); )
      {
         option = (Option) i.next();
         optBuf = new StringBuffer( prefixList.get( x++ ).toString() );

         if ( optBuf.length() < max )
         {
             optBuf.append(createPadding(max - optBuf.length()));
         }
         optBuf.append( dpad );
         
         int nextLineTabStop = max + descPad;
         renderWrappedText(sb, width, nextLineTabStop,
                           optBuf.append(option.getDescription()).toString());
         if ( i.hasNext() )
         {
             sb.append(defaultNewLine);
         }
      }

      return sb;
   }

   protected StringBuffer renderWrappedText( StringBuffer sb,
                                             int width,
                                             int nextLineTabStop,
                                             String text )
   {
      int pos = findWrapPos( text, width, 0);
      if ( pos == -1 )
      {
         sb.append(rtrim(text));
         return sb;
      }
      else
      {
         sb.append(rtrim(text.substring(0, pos))).append(defaultNewLine);
      }

      //all following lines must be padded with nextLineTabStop space characters
      final String padding = createPadding(nextLineTabStop);

      while ( true )
      {
         text = padding + text.substring(pos).trim();
         pos = findWrapPos( text, width, nextLineTabStop );
         if ( pos == -1 )
         {
            sb.append(text);
            return sb;
         }

         sb.append(rtrim(text.substring(0, pos))).append(defaultNewLine);
      }

   }

   /**
    * Finds the next text wrap position after <code>startPos</code> for the text
    * in <code>sb</code> with the column width <code>width</code>.
    * The wrap point is the last postion before startPos+width having a 
whitespace
    * character (space, \n, \r).
    *
    * @param sb text to be analyzed
    * @param width width of the wrapped text
    * @param startPos position from which to start the lookup whitespace 
character
    * @return postion on which the text must be wrapped or -1 if the wrap 
position is at the end
    *         of the text
    */
   protected int findWrapPos( String text, int width, int startPos )
   {
      int pos = -1;
      // the line ends before the max wrap pos or a new line char found
      if ( ((pos = text.indexOf('\n', startPos)) != -1 && pos <= width)  ||
           ((pos = text.indexOf('\t', startPos)) != -1 && pos <= width) )
      {
         return pos;
      }
      else if ( (startPos + width) >= text.length() )
      {
         return -1;
      }

      //look for the last whitespace character before startPos+width
      pos = startPos + width;
      char c;
      while ( pos >= startPos && (c = text.charAt(pos)) != ' ' && c != '\n' && 
c != '\r' )
      {
         --pos;
      }
      //if we found it - just return
      if ( pos > startPos )
      {
         return pos;
      }
      else
      {
         //must look for the first whitespace chearacter after startPos + width
         pos = startPos + width;
         while ( pos <= text.length() && (c = text.charAt(pos)) != ' ' && c != 
'\n' && c != '\r' )
         {
            ++pos;
         }
         return pos == text.length() ? -1 : pos;
      }
   }

   protected String createPadding(int len)
   {
      StringBuffer sb = new StringBuffer(len);
      for ( int i = 0; i < len; ++i )
      {
         sb.append(' ');
      }
      return sb.toString();
   }

   protected String rtrim( String s )
   {
      if ( s == null || s.length() == 0 )
      {
         return s;
      }

      int pos = s.length();
      while ( pos >= 0 && Character.isWhitespace(s.charAt(pos-1)) )
      {
         --pos;
      }
      return s.substring(0, pos);
   }

   // ------------------------------------------------------- Package protected
   
   // ----------------------------------------------------------------- Private
   
   // ----------------------------------------------------------- Inner classes

    private static class StringBufferComparator
    implements Comparator
    {
        public int compare( Object o1, Object o2 )
        {
            String str1 = stripPrefix(o1.toString());
            String str2 = stripPrefix(o2.toString());
            return (str1.compareTo(str2));
        }

        private String stripPrefix(String strOption)
        {
            // Strip any leading '-' characters
            int iStartIndex = strOption.lastIndexOf('-');
            if (iStartIndex == -1)
            {
              iStartIndex = 0;
            }
            return strOption.substring(iStartIndex);

        }
    }
}
/*
 * $Header: 
/home/cvs/jakarta-commons-sandbox/cli/src/java/org/apache/commons/cli/Options.java,v
 1.5 2002/06/06 22:32:37 bayard Exp $
 * $Revision: 1.5 $
 * $Date: 2002/06/06 22:32:37 $
 *
 * ====================================================================
 *
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Commons", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact [EMAIL PROTECTED]
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 */

package org.apache.commons.cli;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.*;

/** <p>Main entry-point into the library.</p>
 *
 * <p>Options represents a collection of [EMAIL PROTECTED] Option} objects, 
which
 * describe the possible options for a command-line.<p>
 *
 * <p>It may flexibly parse long and short options, with or without
 * values.  Additionally, it may parse only a portion of a commandline,
 * allowing for flexible multi-stage parsing.<p>
 *
 * @see org.apache.commons.cli.CommandLine
 *
 * @author bob mcwhirter (bob @ werken.com)
 * @author <a href="mailto:[EMAIL PROTECTED]">James Strachan</a>
 * @version $Revision: 1.5 $
 */
public class Options {

    /** a map of the options with the character key */
    private Map  shortOpts    = new LinkedHashMap();

    /** a map of the options with the long key */
    private Map  longOpts     = new LinkedHashMap();

    /** a map of the required options */
    private List requiredOpts = new ArrayList();
    
    /** a map of the option groups */
    private Map optionGroups  = new LinkedHashMap();
    
    /* jp: changed 3 HashMaps to LinkedHashMaps above, to preserve order of 
addition
     * of options when iterating in HelpFormatter. */

    /** <p>Construct a new Options descriptor</p>
     */
    public Options() {        
    }

    /**
     * <p>Add the specified option group.</p>
     *
     * @param group the OptionGroup that is to be added
     * @return the resulting Options instance
     */
    public Options addOptionGroup( OptionGroup group ) {
        Iterator options = group.getOptions().iterator();

        if( group.isRequired() ) {
            requiredOpts.add( group );
        }

        while( options.hasNext() ) {
            Option option = (Option)options.next();
            // an Option cannot be required if it is in an
            // OptionGroup, either the group is required or
            // nothing is required
            option.setRequired( false );
            addOption( option );

            optionGroups.put( option.getOpt(), group );
        }

        return this;
    }

    /** <p>Add an option that only contains a short-name</p>
     * <p>It may be specified as requiring an argument.</p>
     *
     * @param opt Short single-character name of the option.
     * @param hasArg flag signally if an argument is required after this option
     * @param description Self-documenting description
     * @return the resulting Options instance
     */
    public Options addOption(String opt, boolean hasArg, String description) {
        addOption( opt, null, hasArg, description );
        return this;
    }
    
    /** <p>Add an option that contains a short-name and a long-name</p>
     * <p>It may be specified as requiring an argument.</p>
     *
     * @param opt Short single-character name of the option.
     * @param longOpt Long multi-character name of the option.
     * @param hasArg flag signally if an argument is required after this option
     * @param description Self-documenting description
     * @return the resulting Options instance
     */
    public Options addOption(String opt, String longOpt, boolean hasArg, String 
description) {
        addOption( new Option( opt, longOpt, hasArg, description ) );        
        return this;
    }

    /**
     * <p>Adds an option instance</p>
     *
     * @param opt the option that is to be added 
     * @return the resulting Options instance
     */
    public Options addOption(Option opt)  {
        String shortOpt = "-" + opt.getOpt();
        
        // add it to the long option list
        if ( opt.hasLongOpt() ) {
            longOpts.put( "--" + opt.getLongOpt(), opt );
        }
        
        // if the option is required add it to the required list
        if ( opt.isRequired() ) {
            requiredOpts.add( shortOpt );
        }

        shortOpts.put( shortOpt, opt );
        
        return this;
    }
    
    /** <p>Retrieve a read-only list of options in this set</p>
     *
     * @return read-only Collection of [EMAIL PROTECTED] Option} objects in 
this descriptor
     */
    public Collection getOptions() {
        List opts = new ArrayList( shortOpts.values() );

        // now look through the long opts to see if there are any Long-opt
        // only options
        Iterator iter = longOpts.values().iterator();
        while (iter.hasNext())
        {
            Object item = iter.next();
            if (!opts.contains(item))
            {
                opts.add(item);
            }
        }
        return Collections.unmodifiableCollection( opts );
    }

    /**
     * <p>Returns the Options for use by the HelpFormatter.</p>
     *
     * @return the List of Options
     */
    List helpOptions() {
        return new ArrayList( shortOpts.values() );
    }

    /** <p>Returns the required options as a 
     * <code>java.util.Collection</code>.</p>
     *
     * @return Collection of required options
     */
    public List getRequiredOptions() {
        return requiredOpts;
    }
    
    /** <p>Retrieve the named [EMAIL PROTECTED] Option}</p>
     *
     * @param opt short or long name of the [EMAIL PROTECTED] Option}
     * @return the option represented by opt
     */
    public Option getOption( String opt ) {

        Option option = null;

        // short option
        if( opt.length() == 1 ) {
            option = (Option)shortOpts.get( "-" + opt );
        }
        // long option
        else if( opt.startsWith( "--" ) ) {
            option = (Option)longOpts.get( opt );
        }
        // a just-in-case
        else {
            option = (Option)shortOpts.get( opt );
        }

        return (option == null) ? null : (Option)option.clone();
    }

    /** 
     * <p>Returns whether the named [EMAIL PROTECTED] Option} is a member
     * of this [EMAIL PROTECTED] Options}</p>
     *
     * @param opt short or long name of the [EMAIL PROTECTED] Option}
     * @return true if the named [EMAIL PROTECTED] Option} is a member
     * of this [EMAIL PROTECTED] Options}
     */
    public boolean hasOption( String opt ) {

        // short option
        if( opt.length() == 1 ) {
            return shortOpts.containsKey( "-" + opt );
        }
        // long option
        else if( opt.startsWith( "--" ) ) {
            return longOpts.containsKey( opt );
        }
        // a just-in-case
        else {
            return shortOpts.containsKey( opt );
        }
    }

    /** <p>Returns the OptionGroup the <code>opt</code>
     * belongs to.</p>
     * @param opt the option whose OptionGroup is being queried.
     *
     * @return the OptionGroup if <code>opt</code> is part
     * of an OptionGroup, otherwise return null
     */
    public OptionGroup getOptionGroup( Option opt ) {
        return (OptionGroup)optionGroups.get( opt.getOpt() );
    }
    
    /** <p>Dump state, suitable for debugging.</p>
     *
     * @return Stringified form of this object
     */
    public String toString() {
        StringBuffer buf = new StringBuffer();
        
        buf.append("[ Options: [ short ");
        buf.append( shortOpts.toString() );
        buf.append( " ] [ long " );
        buf.append( longOpts );
        buf.append( " ]");
        
        return buf.toString();
    }
}
/*
 * $Header: 
/home/cvs/jakarta-commons-sandbox/cli/src/java/org/apache/commons/cli/OptionGroup.java,v
 1.2 2002/06/06 09:37:26 jstrachan Exp $
 * $Revision: 1.2 $
 * $Date: 2002/06/06 09:37:26 $
 *
 * ====================================================================
 *
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Commons", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact [EMAIL PROTECTED]
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 */

package org.apache.commons.cli;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;

/**
 * A group of mutually exclusive options.
 * @author John Keyes ( john at integralsource.com )
 * @version $Revision: 1.2 $
 */
public class OptionGroup {

    /** hold the options */
    private HashMap optionMap = new LinkedHashMap();
    /* jp: changed to LinkedHashMap */

    /** the name of the selected option */
    private String selected;

    /** specified whether this group is required */
    private boolean required;

    /**
     * add <code>opt</code> to this group
     *
     * @param opt the option to add to this group
     * @return this option group with opt added
     */
    public OptionGroup addOption(Option opt) {
        // key   - option name
        // value - the option
        optionMap.put( "-" + opt.getOpt(), opt );
        return this;
    }
    
    /* jp: added this accessor */
    /**
     * Access an option in this group by short name.  It is not necessary to 
include the
     * leading '-' character, but the access will still work if included.  
Returns
     * <code>null</code> if <code>shortName</code> does not identify an option 
in this
     * group.
     *
     * @param shortName short name of an option in this group.
     *
     * @return the named option, or <code>null</code>
     */
    public Option getOption(String shortName) {
        if (!shortName.startsWith("-")) {
            shortName = "-" + shortName;
        }
        return (Option) optionMap.get(shortName);
    }

    /**
     * @return the names of the options in this group as a 
     * <code>Collection</code>
     */
    public Collection getNames() {
        // the key set is the collection of names
        return optionMap.keySet();
    }

    /**
     * @return the options in this group as a <code>Collection</code>
     */
    public Collection getOptions() {
        // the values are the collection of options
        return optionMap.values();
    }

    /**
     * set the selected option of this group to <code>name</code>.
     * @param opt the option that is selected
     * @throws AlreadySelectedException if an option from this group has 
     * already been selected.
     */
    public void setSelected(Option opt) throws AlreadySelectedException {
        // if no option has already been selected or the 
        // same option is being reselected then set the
        // selected member variable

        if ( this.selected == null || this.selected.equals( opt.getOpt() ) ) {
            this.selected = opt.getOpt();
        }
        else {
            throw new AlreadySelectedException( "an option from this group has 
" + 
                                                "already been selected: '" + 
                                                selected + "'");
        }
    }

    /**
     * @return the selected option name
     */
    public String getSelected() {
        return selected;
    }

    /**
     * @param required specifies if this group is required
     */
    public void setRequired( boolean required ) {
        this.required = required;
    }

    /**
     * Returns whether this option group is required.
     *
     * @returns whether this option group is required
     */
    public boolean isRequired() {
        return this.required;
    }

    /**
     * <p>Returns the stringified version of this OptionGroup.</p>
     * @return the stringified representation of this group
     */
    public String toString() {
        StringBuffer buff = new StringBuffer();

        Iterator iter = getOptions().iterator();

        buff.append( "[" );
        while( iter.hasNext() ) {
            Option option = (Option)iter.next();

            buff.append( "-" );
            buff.append( option.getOpt() );
            buff.append( " " );
            buff.append( option.getDescription( ) );

            if( iter.hasNext() ) {
                buff.append( ", " );
            }
        }
        buff.append( "]" );

        return buff.toString();
    }
}

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to