Coverage Report - org.crosswire.common.options.GetOptions
 
Classes in this File Line Coverage Branch Coverage Complexity
GetOptions
0%
0/94
0%
0/50
7.2
 
 1  
 /**
 2  
  * Distribution License:
 3  
  * JSword is free software; you can redistribute it and/or modify it under
 4  
  * the terms of the GNU Lesser General Public License, version 2.1 or later
 5  
  * as published by the Free Software Foundation. This program is distributed
 6  
  * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 7  
  * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 8  
  * See the GNU Lesser General Public License for more details.
 9  
  *
 10  
  * The License is available on the internet at:
 11  
  *      http://www.gnu.org/copyleft/lgpl.html
 12  
  * or by writing to:
 13  
  *      Free Software Foundation, Inc.
 14  
  *      59 Temple Place - Suite 330
 15  
  *      Boston, MA 02111-1307, USA
 16  
  *
 17  
  * © CrossWire Bible Society, 2008 - 2016
 18  
  *
 19  
  */
 20  
 package org.crosswire.common.options;
 21  
 
 22  
 import java.util.ArrayList;
 23  
 import java.util.LinkedHashMap;
 24  
 import java.util.List;
 25  
 import java.util.Map;
 26  
 
 27  
 /**
 28  
  * GetOptions parses an argument list for requested arguments given by an
 29  
  * OptionList.<br><br>
 30  
  * 
 31  
  * This supports short and long options:<br>
 32  
  * Short Options have the following characteristics.
 33  
  * <ul>
 34  
  * <li>A single dash, '-', starts a flag or a flag sequence. An example of a
 35  
  * flag is '-c' and a flag sequence is '-xyz'.</li>
 36  
  * <li>A flag may have a required argument. The flag may or may not be separated
 37  
  * by a space from it's argument. For example, both -fbar and -f bar are
 38  
  * acceptable.</li>
 39  
  * <li>A flag may have an optional argument. The flag must not be separated by a
 40  
  * space from it's optional argument. For example, -fbar is acceptable provides
 41  
  * bar as the argument, but -f bar has bar as a non-option argument.</li>
 42  
  * <li>These rules can combine. For example, -xyzfoo can be the same as -x -y -z
 43  
  * foo</li>
 44  
  * <li>If an Option expects an argument, then that argument can have a leading
 45  
  * '-'. That is, if -x requires an option then the argument -y can be given as
 46  
  * -x-y or -x -y.</li>
 47  
  * </ul>
 48  
  * 
 49  
  * Long Options have the following characteristics:
 50  
  * <ul>
 51  
  * <li>A double dash '--' starts a single flag. For example --print. Note, a
 52  
  * long option is typically descriptive, but can be a single character.</li>
 53  
  * <li>An argument may be given in one of two ways --file=filename or --file
 54  
  * filename. That is, separated by an '=' sign or whitespace.</li>
 55  
  * </ul>
 56  
  * Note:
 57  
  * <ul>
 58  
  * <li>Options can be repeated. What that means is up to the program.</li>
 59  
  * <li>The '--' sequence terminates argument processing.</li>
 60  
  * <li>A '-' by itself is not a flag.</li>
 61  
  * <li>Unrecognized flags are an error.</li>
 62  
  * <li>Unrecognized arguments are moved after the processed flags.</li>
 63  
  * </ul>
 64  
  * 
 65  
  * @see gnu.lgpl.License The GNU Lesser General Public License for details.
 66  
  * @author DM Smith
 67  
  */
 68  
 public class GetOptions {
 69  0
     public GetOptions(String programName, String[] args, OptionList programOptions) {
 70  0
         this.programName = programName;
 71  0
         this.args = args.clone();
 72  0
         this.programOptions = programOptions;
 73  
         // Initially, we have not started to process an argument
 74  0
         this.nonOptionArgs = new ArrayList<String>();
 75  0
         this.suppliedOptions = new LinkedHashMap<Option, String>();
 76  
 
 77  0
         parse();
 78  0
     }
 79  
 
 80  
     /**
 81  
      * @return the programName
 82  
      */
 83  
     public String getProgramName() {
 84  0
         return programName;
 85  
     }
 86  
 
 87  
     /**
 88  
      * @param programName
 89  
      *            the programName to set
 90  
      */
 91  
     public void setProgramName(String programName) {
 92  0
         this.programName = programName;
 93  0
     }
 94  
 
 95  
     private void parse() {
 96  0
         int nargs = args.length;
 97  0
         int skip = 0;
 98  0
         for (int i = 0; i < nargs; i += 1 + skip) {
 99  0
             skip = 0;
 100  0
             String nextArg = args[i];
 101  
             // All options are 2 or more characters long and begin with a '-'.
 102  
             // If this is a non-option then note it and advance
 103  0
             if (nextArg.length() < 2 || nextArg.charAt(0) != '-') {
 104  0
                 nonOptionArgs.add(nextArg);
 105  0
                 continue;
 106  
             }
 107  
 
 108  
             // If we are at the end of all options, '--', we need to skip this
 109  
             // and copy what follows to the end
 110  0
             if ("--".equals(nextArg)) {
 111  0
                 for (int j = i + 1; j < nargs; j++) {
 112  0
                     nonOptionArgs.add(args[j]);
 113  
                 }
 114  0
                 return;
 115  
             }
 116  
 
 117  
             // At this point we are on a short option, a short option sequence
 118  
             // or a long option.
 119  
             // Invariant: the length > 1.
 120  0
             if (nextArg.charAt(1) == '-') {
 121  
                 // Process a long argument
 122  
                 // This can be of the form --flag or --flag argument or
 123  
                 // --flag=argument
 124  0
                 int equalPos = nextArg.indexOf('=');
 125  0
                 String flag = (equalPos != -1) ? nextArg.substring(2, equalPos) : nextArg.substring(2);
 126  0
                 List<Option> opts = programOptions.getLongOptions(flag);
 127  0
                 int count = opts.size();
 128  0
                 if (count == 0) {
 129  0
                     throw new IllegalArgumentException("Illegal option --" + flag);
 130  
                 }
 131  0
                 if (count > 1) {
 132  0
                     throw new IllegalArgumentException("Ambiguous option --" + flag);
 133  
                 }
 134  0
                 Option option = opts.get(0);
 135  0
                 if (option.getArgumentType().equals(ArgumentType.NO_ARGUMENT)) {
 136  
                     // Add option with null argument to options
 137  0
                     suppliedOptions.put(option, null);
 138  0
                     continue;
 139  
                 }
 140  
                 // An argument is allowed or required
 141  0
                 if (equalPos != -1) {
 142  
                     // Add option with argument to options
 143  
                     // Check for empty argument
 144  0
                     String argument = (equalPos + 1 < nextArg.length()) ? nextArg.substring(equalPos + 1) : "";
 145  0
                     suppliedOptions.put(option, argument);
 146  0
                     continue;
 147  
                 }
 148  
                 // An argument is required, so take the next one.
 149  0
                 if (option.getArgumentType().equals(ArgumentType.REQUIRED_ARGUMENT)) {
 150  0
                     if (i + 1 < nargs) {
 151  
                         // Add option with following argument to options
 152  0
                         String argument = args[i];
 153  0
                         skip = 1;
 154  0
                         suppliedOptions.put(option, argument);
 155  0
                         continue;
 156  
                     }
 157  0
                     throw new IllegalArgumentException("Option missing required argument");
 158  
                 }
 159  0
             } else {
 160  
                 // Process a short argument or short argument sequence
 161  
 
 162  
                 // for each letter after the '-'
 163  0
                 int shortSeqSize = nextArg.length();
 164  0
                 for (int j = 1; j < shortSeqSize; j++) {
 165  0
                     char curChar = nextArg.charAt(j);
 166  0
                     Option option = programOptions.getShortOption(curChar);
 167  0
                     if (option == null) {
 168  0
                         throw new IllegalArgumentException("Illegal option -" + curChar);
 169  
                     }
 170  0
                     if (option.getArgumentType().equals(ArgumentType.NO_ARGUMENT)) {
 171  
                         // Add option with null argument to options
 172  0
                         suppliedOptions.put(option, null);
 173  0
                         continue;
 174  
                     }
 175  
                     // This option allows or requires an argument
 176  0
                     if (j < shortSeqSize) {
 177  
                         // since there is stuff that follows the flag, it is the
 178  
                         // argument.
 179  0
                         String argument = nextArg.substring(j + 1);
 180  0
                         suppliedOptions.put(option, argument);
 181  0
                         continue;
 182  
                     }
 183  0
                     if (option.getArgumentType().equals(ArgumentType.REQUIRED_ARGUMENT)) {
 184  0
                         if (i + 1 < nargs) {
 185  
                             // Add option with following argument to options
 186  0
                             String argument = args[i];
 187  0
                             skip = 1;
 188  0
                             suppliedOptions.put(option, argument);
 189  0
                             continue;
 190  
                         }
 191  0
                         throw new IllegalArgumentException("Option missing required argument");
 192  
                     }
 193  
                 }
 194  
             }
 195  
         }
 196  0
     }
 197  
 
 198  
     /**
 199  
      * Swap adjacent blocks in an array.
 200  
      * 
 201  
      * @param array
 202  
      *            The array to modify in place
 203  
      * @param firstStart
 204  
      *            the index of the start of the first block
 205  
      * @param firstEnd
 206  
      *            the index of the end of the first block
 207  
      * @param secondEnd
 208  
      *            the index of the end of the second block. Note: the start of
 209  
      *            the second block is firstEnd + 1
 210  
      */
 211  
     public static void swap(Object[] array, int firstStart, int firstEnd, int secondEnd) {
 212  
         // Note: this is currently unused.
 213  
         // If we implement the traditional GNU extensions GetOpts interface we
 214  
         // will need it.
 215  
         // We copy the smaller block to the longer block.
 216  
 
 217  
         // The performance of this is linear with respect to the size of the
 218  
         // larger block.
 219  
         // If the blocks are equal the number of swaps is equal to the "larger"
 220  
         // block size otherwise it is one greater.
 221  
 
 222  
         // if the first block is smaller we start at the start of both and swap
 223  
         // Otherwise we start at the end of both and swap from the end to the
 224  
         // start
 225  
 
 226  
         // Set variables for the second block to be larger
 227  0
         int sourcePos = firstStart;
 228  0
         int destPos = firstEnd + 1;
 229  0
         int increment = 1;
 230  0
         int destStop = secondEnd;
 231  0
         int firstSize = firstEnd - firstStart + 1;
 232  0
         int secondSize = secondEnd - firstEnd;
 233  0
         int swapCount = secondSize + 1;
 234  
 
 235  0
         if (firstSize > secondSize) {
 236  
             // first block is bigger or equal
 237  0
             sourcePos = secondEnd;
 238  0
             destPos = firstEnd;
 239  0
             destStop = firstStart;
 240  0
             increment = -1;
 241  0
             swapCount = firstSize + 1;
 242  
         }
 243  
 
 244  0
         if (firstSize == secondSize) {
 245  0
             swapCount--;
 246  
         }
 247  
 
 248  0
         while (swapCount-- > 0) {
 249  0
             Object temp = array[destPos];
 250  0
             array[destPos] = array[sourcePos];
 251  0
             array[sourcePos] = temp;
 252  0
             if (sourcePos != destStop) {
 253  0
                 sourcePos += increment;
 254  
             }
 255  0
             if (destPos != destStop) {
 256  0
                 destPos += increment;
 257  
             }
 258  
 
 259  0
         }
 260  0
     }
 261  
 
 262  
     // public static void main(String[] args)
 263  
     // {
 264  
     // String[] a = {"a","b","c","d","e"};
 265  
     // swap(a, 0, 2, 4);
 266  
     // swap(a, 0, 1, 4);
 267  
     // swap(a, 0, 0, 1);
 268  
     // swap(a, 0, 0, 1);
 269  
     // swap(a, 1, 2, 4);
 270  
     // swap(a, 1, 2, 4);
 271  
     // }
 272  
 
 273  
     private String programName;
 274  
     private String[] args;
 275  
     private OptionList programOptions;
 276  
 
 277  
     /**
 278  
      * The position in the array that is currently being studied.
 279  
      */
 280  
     private List<String> nonOptionArgs;
 281  
     private Map<Option, String> suppliedOptions;
 282  
 }