Home » jakarta-jmeter-2.3.4_src » org.apache.commons » cli » avalon » [javadoc | source]

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *   http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed  under the  License is distributed on an "AS IS" BASIS,
   13    * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
   14    * implied.
   15    *
   16    * See the License for the specific language governing permissions and
   17    * limitations under the License.
   18    */
   19   package org.apache.commons.cli.avalon;
   20   
   21   // Renamed from org.apache.avalon.excalibur.cli
   22   
   23   import java.text.ParseException;
   24   import java.util.Hashtable;
   25   import java.util.Vector;
   26   
   27   /**
   28    * Parser for command line arguments.
   29    *
   30    * This parses command lines according to the standard (?) of GNU utilities.
   31    *
   32    * Note: This is still used in 1.1 libraries so do not add 1.2+ dependencies.
   33    *
   34    * Note that CLArgs uses a backing hashtable for the options index and so
   35    * duplicate arguments are only returned by getArguments().
   36    *
   37    * @see ParserControl
   38    * @see CLOption
   39    * @see CLOptionDescriptor
   40    */
   41   public final class CLArgsParser {
   42       // cached character == Integer.MAX_VALUE when invalid
   43       private static final int INVALID = Integer.MAX_VALUE;
   44   
   45       private static final int STATE_NORMAL = 0;
   46   
   47       private static final int STATE_REQUIRE_2ARGS = 1;
   48   
   49       private static final int STATE_REQUIRE_ARG = 2;
   50   
   51       private static final int STATE_OPTIONAL_ARG = 3;
   52   
   53       private static final int STATE_NO_OPTIONS = 4;
   54   
   55       private static final int STATE_OPTION_MODE = 5;
   56   
   57       // Values for creating tokens
   58       private static final int TOKEN_SEPARATOR = 0;
   59   
   60       private static final int TOKEN_STRING = 1;
   61   
   62       private static final char[] ARG_SEPARATORS = new char[] { (char) 0, '=' };
   63   
   64       private static final char[] NULL_SEPARATORS = new char[] { (char) 0 };
   65   
   66       private final CLOptionDescriptor[] m_optionDescriptors;
   67   
   68       private final Vector m_options;
   69   
   70       private Hashtable m_optionIndex;
   71   
   72       private final ParserControl m_control;
   73   
   74       private String m_errorMessage;
   75   
   76       private String[] m_unparsedArgs = new String[] {};
   77   
   78       // variables used while parsing options.
   79       private char m_ch;
   80   
   81       private String[] m_args;
   82   
   83       private boolean m_isLong;
   84   
   85       private int m_argIndex;
   86   
   87       private int m_stringIndex;
   88   
   89       private int m_stringLength;
   90   
   91       private int m_lastChar = INVALID;
   92   
   93       private int m_lastOptionId;
   94   
   95       private CLOption m_option;
   96   
   97       private int m_state = STATE_NORMAL;
   98   
   99       /**
  100        * Retrieve an array of arguments that have not been parsed due to the
  101        * parser halting.
  102        *
  103        * @return an array of unparsed args
  104        */
  105       public final String[] getUnparsedArgs() {
  106           return m_unparsedArgs;
  107       }
  108   
  109       /**
  110        * Retrieve a list of options that were parsed from command list.
  111        *
  112        * @return the list of options
  113        */
  114       public final Vector getArguments() {
  115           // System.out.println( "Arguments: " + m_options );
  116           return m_options;
  117       }
  118   
  119       /**
  120        * Retrieve the {@link CLOption} with specified id, or <code>null</code>
  121        * if no command line option is found.
  122        *
  123        * @param id
  124        *            the command line option id
  125        * @return the {@link CLOption} with the specified id, or <code>null</code>
  126        *         if no CLOption is found.
  127        * @see CLOption
  128        */
  129       public final CLOption getArgumentById(final int id) {
  130           return (CLOption) m_optionIndex.get(new Integer(id));
  131       }
  132   
  133       /**
  134        * Retrieve the {@link CLOption} with specified name, or <code>null</code>
  135        * if no command line option is found.
  136        *
  137        * @param name
  138        *            the command line option name
  139        * @return the {@link CLOption} with the specified name, or
  140        *         <code>null</code> if no CLOption is found.
  141        * @see CLOption
  142        */
  143       public final CLOption getArgumentByName(final String name) {
  144           return (CLOption) m_optionIndex.get(name);
  145       }
  146   
  147       /**
  148        * Get Descriptor for option id.
  149        *
  150        * @param id
  151        *            the id
  152        * @return the descriptor
  153        */
  154       private final CLOptionDescriptor getDescriptorFor(final int id) {
  155           for (int i = 0; i < m_optionDescriptors.length; i++) {
  156               if (m_optionDescriptors[i].getId() == id) {
  157                   return m_optionDescriptors[i];
  158               }
  159           }
  160   
  161           return null;
  162       }
  163   
  164       /**
  165        * Retrieve a descriptor by name.
  166        *
  167        * @param name
  168        *            the name
  169        * @return the descriptor
  170        */
  171       private final CLOptionDescriptor getDescriptorFor(final String name) {
  172           for (int i = 0; i < m_optionDescriptors.length; i++) {
  173               if (m_optionDescriptors[i].getName().equals(name)) {
  174                   return m_optionDescriptors[i];
  175               }
  176           }
  177   
  178           return null;
  179       }
  180   
  181       /**
  182        * Retrieve an error message that occured during parsing if one existed.
  183        *
  184        * @return the error string
  185        */
  186       public final String getErrorString() {
  187           // System.out.println( "ErrorString: " + m_errorMessage );
  188           return m_errorMessage;
  189       }
  190   
  191       /**
  192        * Require state to be placed in for option.
  193        *
  194        * @param descriptor
  195        *            the Option Descriptor
  196        * @return the state
  197        */
  198       private final int getStateFor(final CLOptionDescriptor descriptor) {
  199           final int flags = descriptor.getFlags();
  200           if ((flags & CLOptionDescriptor.ARGUMENTS_REQUIRED_2) == CLOptionDescriptor.ARGUMENTS_REQUIRED_2) {
  201               return STATE_REQUIRE_2ARGS;
  202           } else if ((flags & CLOptionDescriptor.ARGUMENT_REQUIRED) == CLOptionDescriptor.ARGUMENT_REQUIRED) {
  203               return STATE_REQUIRE_ARG;
  204           } else if ((flags & CLOptionDescriptor.ARGUMENT_OPTIONAL) == CLOptionDescriptor.ARGUMENT_OPTIONAL) {
  205               return STATE_OPTIONAL_ARG;
  206           } else {
  207               return STATE_NORMAL;
  208           }
  209       }
  210   
  211       /**
  212        * Create a parser that can deal with options and parses certain args.
  213        *
  214        * @param args
  215        *            the args, typically that passed to the
  216        *            <code>public static void main(String[] args)</code> method.
  217        * @param optionDescriptors
  218        *            the option descriptors
  219        * @param control
  220        *            the parser control used determine behaviour of parser
  221        */
  222       public CLArgsParser(final String[] args, final CLOptionDescriptor[] optionDescriptors, final ParserControl control) {
  223           m_optionDescriptors = optionDescriptors;
  224           m_control = control;
  225           m_options = new Vector();
  226           m_args = args;
  227   
  228           try {
  229               parse();
  230               checkIncompatibilities(m_options);
  231               buildOptionIndex();
  232           } catch (final ParseException pe) {
  233               m_errorMessage = pe.getMessage();
  234           }
  235   
  236           // System.out.println( "Built : " + m_options );
  237           // System.out.println( "From : " + Arrays.asList( args ) );
  238       }
  239   
  240       /**
  241        * Check for duplicates of an option. It is an error to have duplicates
  242        * unless appropriate flags is set in descriptor.
  243        *
  244        * @param arguments
  245        *            the arguments
  246        */
  247       private final void checkIncompatibilities(final Vector arguments) throws ParseException {
  248           final int size = arguments.size();
  249   
  250           for (int i = 0; i < size; i++) {
  251               final CLOption option = (CLOption) arguments.elementAt(i);
  252               final int id = option.getDescriptor().getId();
  253               final CLOptionDescriptor descriptor = getDescriptorFor(id);
  254   
  255               // this occurs when id == 0 and user has not supplied a descriptor
  256               // for arguments
  257               if (null == descriptor) {
  258                   continue;
  259               }
  260   
  261               final int[] incompatible = descriptor.getIncompatible();
  262   
  263               checkIncompatible(arguments, incompatible, i);
  264           }
  265       }
  266   
  267       private final void checkIncompatible(final Vector arguments, final int[] incompatible, final int original)
  268               throws ParseException {
  269           final int size = arguments.size();
  270   
  271           for (int i = 0; i < size; i++) {
  272               if (original == i) {
  273                   continue;
  274               }
  275   
  276               final CLOption option = (CLOption) arguments.elementAt(i);
  277               final int id = option.getDescriptor().getId();
  278   
  279               for (int j = 0; j < incompatible.length; j++) {
  280                   if (id == incompatible[j]) {
  281                       final CLOption originalOption = (CLOption) arguments.elementAt(original);
  282                       final int originalId = originalOption.getDescriptor().getId();
  283   
  284                       String message = null;
  285   
  286                       if (id == originalId) {
  287                           message = "Duplicate options for " + describeDualOption(originalId) + " found.";
  288                       } else {
  289                           message = "Incompatible options -" + describeDualOption(id) + " and "
  290                                   + describeDualOption(originalId) + " found.";
  291                       }
  292                       throw new ParseException(message, 0);
  293                   }
  294               }
  295           }
  296       }
  297   
  298       private final String describeDualOption(final int id) {
  299           final CLOptionDescriptor descriptor = getDescriptorFor(id);
  300           if (null == descriptor) {
  301               return "<parameter>";
  302           } else {
  303               final StringBuffer sb = new StringBuffer();
  304               boolean hasCharOption = false;
  305   
  306               if (Character.isLetter((char) id)) {
  307                   sb.append('-');
  308                   sb.append((char) id);
  309                   hasCharOption = true;
  310               }
  311   
  312               final String longOption = descriptor.getName();
  313               if (null != longOption) {
  314                   if (hasCharOption) {
  315                       sb.append('/');
  316                   }
  317                   sb.append("--");
  318                   sb.append(longOption);
  319               }
  320   
  321               return sb.toString();
  322           }
  323       }
  324   
  325       /**
  326        * Create a parser that deals with options and parses certain args.
  327        *
  328        * @param args
  329        *            the args
  330        * @param optionDescriptors
  331        *            the option descriptors
  332        */
  333       public CLArgsParser(final String[] args, final CLOptionDescriptor[] optionDescriptors) {
  334           this(args, optionDescriptors, null);
  335       }
  336   
  337       /**
  338        * Create a string array that is subset of input array. The sub-array should
  339        * start at array entry indicated by index. That array element should only
  340        * include characters from charIndex onwards.
  341        *
  342        * @param array
  343        *            the original array
  344        * @param index
  345        *            the cut-point in array
  346        * @param charIndex
  347        *            the cut-point in element of array
  348        * @return the result array
  349        */
  350       private final String[] subArray(final String[] array, final int index, final int charIndex) {
  351           final int remaining = array.length - index;
  352           final String[] result = new String[remaining];
  353   
  354           if (remaining > 1) {
  355               System.arraycopy(array, index + 1, result, 1, remaining - 1);
  356           }
  357   
  358           result[0] = array[index].substring(charIndex - 1);
  359   
  360           return result;
  361       }
  362   
  363       /**
  364        * Actually parse arguments
  365        */
  366       private final void parse() throws ParseException {
  367           if (0 == m_args.length) {
  368               return;
  369           }
  370   
  371           m_stringLength = m_args[m_argIndex].length();
  372   
  373           while (true) {
  374               m_ch = peekAtChar();
  375   
  376               if (m_argIndex >= m_args.length) {
  377                   break;
  378               }
  379   
  380               if (null != m_control && m_control.isFinished(m_lastOptionId)) {
  381                   // this may need mangling due to peeks
  382                   m_unparsedArgs = subArray(m_args, m_argIndex, m_stringIndex);
  383                   return;
  384               }
  385   
  386               if (STATE_OPTION_MODE == m_state) {
  387                   // if get to an arg barrier then return to normal mode
  388                   // else continue accumulating options
  389                   if (0 == m_ch) {
  390                       getChar(); // strip the null
  391                       m_state = STATE_NORMAL;
  392                   } else {
  393                       parseShortOption();
  394                   }
  395               } else if (STATE_NORMAL == m_state) {
  396                   parseNormal();
  397               } else if (STATE_NO_OPTIONS == m_state) {
  398                   // should never get to here when stringIndex != 0
  399                   addOption(new CLOption(m_args[m_argIndex++]));
  400               } else {
  401                   parseArguments();
  402               }
  403           }
  404   
  405           // Reached end of input arguments - perform final processing
  406           if (m_option != null) {
  407               if (STATE_OPTIONAL_ARG == m_state) {
  408                   m_options.addElement(m_option);
  409               } else if (STATE_REQUIRE_ARG == m_state) {
  410                   final CLOptionDescriptor descriptor = getDescriptorFor(m_option.getDescriptor().getId());
  411                   final String message = "Missing argument to option " + getOptionDescription(descriptor);
  412                   throw new ParseException(message, 0);
  413               } else if (STATE_REQUIRE_2ARGS == m_state) {
  414                   if (1 == m_option.getArgumentCount()) {
  415                       m_option.addArgument("");
  416                       m_options.addElement(m_option);
  417                   } else {
  418                       final CLOptionDescriptor descriptor = getDescriptorFor(m_option.getDescriptor().getId());
  419                       final String message = "Missing argument to option " + getOptionDescription(descriptor);
  420                       throw new ParseException(message, 0);
  421                   }
  422               } else {
  423                   throw new ParseException("IllegalState " + m_state + ": " + m_option, 0);
  424               }
  425           }
  426       }
  427   
  428       private final String getOptionDescription(final CLOptionDescriptor descriptor) {
  429           if (m_isLong) {
  430               return "--" + descriptor.getName();
  431           } else {
  432               return "-" + (char) descriptor.getId();
  433           }
  434       }
  435   
  436       private final char peekAtChar() {
  437           if (INVALID == m_lastChar) {
  438               m_lastChar = readChar();
  439           }
  440           return (char) m_lastChar;
  441       }
  442   
  443       private final char getChar() {
  444           if (INVALID != m_lastChar) {
  445               final char result = (char) m_lastChar;
  446               m_lastChar = INVALID;
  447               return result;
  448           } else {
  449               return readChar();
  450           }
  451       }
  452   
  453       private final char readChar() {
  454           if (m_stringIndex >= m_stringLength) {
  455               m_argIndex++;
  456               m_stringIndex = 0;
  457   
  458               if (m_argIndex < m_args.length) {
  459                   m_stringLength = m_args[m_argIndex].length();
  460               } else {
  461                   m_stringLength = 0;
  462               }
  463   
  464               return 0;
  465           }
  466   
  467           if (m_argIndex >= m_args.length) {
  468               return 0;
  469           }
  470   
  471           return m_args[m_argIndex].charAt(m_stringIndex++);
  472       }
  473   
  474       private char m_tokesep; // Keep track of token separator
  475   
  476       private final Token nextToken(final char[] separators) {
  477           m_ch = getChar();
  478   
  479           if (isSeparator(m_ch, separators)) {
  480               m_tokesep=m_ch;
  481               m_ch = getChar();
  482               return new Token(TOKEN_SEPARATOR, null);
  483           }
  484   
  485           final StringBuffer sb = new StringBuffer();
  486   
  487           do {
  488               sb.append(m_ch);
  489               m_ch = getChar();
  490           } while (!isSeparator(m_ch, separators));
  491   
  492           m_tokesep=m_ch;
  493           return new Token(TOKEN_STRING, sb.toString());
  494       }
  495   
  496       private final boolean isSeparator(final char ch, final char[] separators) {
  497           for (int i = 0; i < separators.length; i++) {
  498               if (ch == separators[i]) {
  499                   return true;
  500               }
  501           }
  502   
  503           return false;
  504       }
  505   
  506       private final void addOption(final CLOption option) {
  507           m_options.addElement(option);
  508           m_lastOptionId = option.getDescriptor().getId();
  509           m_option = null;
  510       }
  511   
  512       private final void parseOption(final CLOptionDescriptor descriptor, final String optionString)
  513               throws ParseException {
  514           if (null == descriptor) {
  515               throw new ParseException("Unknown option " + optionString, 0);
  516           }
  517   
  518           m_state = getStateFor(descriptor);
  519           m_option = new CLOption(descriptor);
  520   
  521           if (STATE_NORMAL == m_state) {
  522               addOption(m_option);
  523           }
  524       }
  525   
  526       private final void parseShortOption() throws ParseException {
  527           m_ch = getChar();
  528           final CLOptionDescriptor descriptor = getDescriptorFor(m_ch);
  529           m_isLong = false;
  530           parseOption(descriptor, "-" + m_ch);
  531   
  532           if (STATE_NORMAL == m_state) {
  533               m_state = STATE_OPTION_MODE;
  534           }
  535       }
  536   
  537       private final void parseArguments() throws ParseException {
  538           if (STATE_REQUIRE_ARG == m_state) {
  539               if ('=' == m_ch || 0 == m_ch) {
  540                   getChar();
  541               }
  542   
  543               final Token token = nextToken(NULL_SEPARATORS);
  544               m_option.addArgument(token.getValue());
  545   
  546               addOption(m_option);
  547               m_state = STATE_NORMAL;
  548           } else if (STATE_OPTIONAL_ARG == m_state) {
  549               if ('-' == m_ch || 0 == m_ch) {
  550                   getChar(); // consume stray character
  551                   addOption(m_option);
  552                   m_state = STATE_NORMAL;
  553                   return;
  554               }
  555   
  556               if (m_isLong && '=' != m_tokesep){ // Long optional arg must have = as separator
  557                   addOption(m_option);
  558                   m_state = STATE_NORMAL;
  559                   return;
  560               }
  561   
  562               if ('=' == m_ch) {
  563                   getChar();
  564               }
  565   
  566               final Token token = nextToken(NULL_SEPARATORS);
  567               m_option.addArgument(token.getValue());
  568   
  569               addOption(m_option);
  570               m_state = STATE_NORMAL;
  571           } else if (STATE_REQUIRE_2ARGS == m_state) {
  572               if (0 == m_option.getArgumentCount()) {
  573                   /*
  574                    * Fix bug: -D arg1=arg2 was causing parse error; however
  575                    * --define arg1=arg2 is OK This seems to be because the parser
  576                    * skips the terminator for the long options, but was not doing
  577                    * so for the short options.
  578                    */
  579                   if (!m_isLong) {
  580                       if (0 == peekAtChar()) {
  581                           getChar();
  582                       }
  583                   }
  584                   final Token token = nextToken(ARG_SEPARATORS);
  585   
  586                   if (TOKEN_SEPARATOR == token.getType()) {
  587                       final CLOptionDescriptor descriptor = getDescriptorFor(m_option.getDescriptor().getId());
  588                       final String message = "Unable to parse first argument for option "
  589                               + getOptionDescription(descriptor);
  590                       throw new ParseException(message, 0);
  591                   } else {
  592                       m_option.addArgument(token.getValue());
  593                   }
  594                   // Are we about to start a new option?
  595                   if (0 == m_ch && '-' == peekAtChar()) {
  596                       // Yes, so the second argument is missing
  597                       m_option.addArgument("");
  598                       m_options.addElement(m_option);
  599                       m_state = STATE_NORMAL;
  600                   }
  601               } else // 2nd argument
  602               {
  603                   final StringBuffer sb = new StringBuffer();
  604   
  605                   m_ch = getChar();
  606                   while (!isSeparator(m_ch, NULL_SEPARATORS)) {
  607                       sb.append(m_ch);
  608                       m_ch = getChar();
  609                   }
  610   
  611                   final String argument = sb.toString();
  612   
  613                   // System.out.println( "Arguement:" + argument );
  614   
  615                   m_option.addArgument(argument);
  616                   addOption(m_option);
  617                   m_option = null;
  618                   m_state = STATE_NORMAL;
  619               }
  620           }
  621       }
  622   
  623       /**
  624        * Parse Options from Normal mode.
  625        */
  626       private final void parseNormal() throws ParseException {
  627           if ('-' != m_ch) {
  628               // Parse the arguments that are not options
  629               final String argument = nextToken(NULL_SEPARATORS).getValue();
  630               addOption(new CLOption(argument));
  631               m_state = STATE_NORMAL;
  632           } else {
  633               getChar(); // strip the -
  634   
  635               if (0 == peekAtChar()) {
  636                   throw new ParseException("Malformed option -", 0);
  637               } else {
  638                   m_ch = peekAtChar();
  639   
  640                   // if it is a short option then parse it else ...
  641                   if ('-' != m_ch) {
  642                       parseShortOption();
  643                   } else {
  644                       getChar(); // strip the -
  645                       // -- sequence .. it can either mean a change of state
  646                       // to STATE_NO_OPTIONS or else a long option
  647   
  648                       if (0 == peekAtChar()) {
  649                           getChar();
  650                           m_state = STATE_NO_OPTIONS;
  651                       } else {
  652                           // its a long option
  653                           final String optionName = nextToken(ARG_SEPARATORS).getValue();
  654                           final CLOptionDescriptor descriptor = getDescriptorFor(optionName);
  655                           m_isLong = true;
  656                           parseOption(descriptor, "--" + optionName);
  657                       }
  658                   }
  659               }
  660           }
  661       }
  662   
  663       /**
  664        * Build the m_optionIndex lookup map for the parsed options.
  665        */
  666       private final void buildOptionIndex() {
  667           final int size = m_options.size();
  668           m_optionIndex = new Hashtable(size * 2);
  669   
  670           for (int i = 0; i < size; i++) {
  671               final CLOption option = (CLOption) m_options.get(i);
  672               final CLOptionDescriptor optionDescriptor = getDescriptorFor(option.getDescriptor().getId());
  673   
  674               m_optionIndex.put(new Integer(option.getDescriptor().getId()), option);
  675   
  676               if (null != optionDescriptor && null != optionDescriptor.getName()) {
  677                   m_optionIndex.put(optionDescriptor.getName(), option);
  678               }
  679           }
  680       }
  681   }

Home » jakarta-jmeter-2.3.4_src » org.apache.commons » cli » avalon » [javadoc | source]