Coverage Report - joptsimple.OptionSet
 
Classes in this File Line Coverage Branch Coverage Complexity
OptionSet
100%
78/78
96%
49/51
2.5
 
 1  
 /*
 2  
  The MIT License
 3  
 
 4  
  Copyright (c) 2004-2015 Paul R. Holser, Jr.
 5  
 
 6  
  Permission is hereby granted, free of charge, to any person obtaining
 7  
  a copy of this software and associated documentation files (the
 8  
  "Software"), to deal in the Software without restriction, including
 9  
  without limitation the rights to use, copy, modify, merge, publish,
 10  
  distribute, sublicense, and/or sell copies of the Software, and to
 11  
  permit persons to whom the Software is furnished to do so, subject to
 12  
  the following conditions:
 13  
 
 14  
  The above copyright notice and this permission notice shall be
 15  
  included in all copies or substantial portions of the Software.
 16  
 
 17  
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 18  
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 19  
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 20  
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 21  
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 22  
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 23  
  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 24  
 */
 25  
 
 26  
 package joptsimple;
 27  
 
 28  
 import java.util.ArrayList;
 29  
 import java.util.HashMap;
 30  
 import java.util.IdentityHashMap;
 31  
 import java.util.List;
 32  
 import java.util.Map;
 33  
 
 34  
 import static java.util.Collections.*;
 35  
 import static joptsimple.internal.Objects.*;
 36  
 
 37  
 /**
 38  
  * Representation of a group of detected command line options, their arguments, and non-option arguments.
 39  
  *
 40  
  * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
 41  
  */
 42  
 public class OptionSet {
 43  
     private final List<OptionSpec<?>> detectedSpecs;
 44  
     private final Map<String, AbstractOptionSpec<?>> detectedOptions;
 45  
     private final Map<AbstractOptionSpec<?>, List<String>> optionsToArguments;
 46  
     private final Map<String, AbstractOptionSpec<?>> recognizedSpecs;
 47  
     private final Map<String, List<?>> defaultValues;
 48  
 
 49  
     /*
 50  
      * Package-private because clients don't create these.
 51  
      */
 52  432
     OptionSet( Map<String, AbstractOptionSpec<?>> recognizedSpecs ) {
 53  432
         detectedSpecs = new ArrayList<OptionSpec<?>>();
 54  432
         detectedOptions = new HashMap<String, AbstractOptionSpec<?>>();
 55  432
         optionsToArguments = new IdentityHashMap<AbstractOptionSpec<?>, List<String>>();
 56  432
         defaultValues = defaultValues( recognizedSpecs );
 57  432
         this.recognizedSpecs = recognizedSpecs;
 58  432
     }
 59  
 
 60  
     /**
 61  
      * Tells whether any options were detected.
 62  
      *
 63  
      * @return {@code true} if any options were detected
 64  
      */
 65  
     public boolean hasOptions() {
 66  2
         return !( detectedOptions.size() == 1 && detectedOptions.values().iterator().next().representsNonOptions() );
 67  
     }
 68  
 
 69  
     /**
 70  
      * Tells whether the given option was detected.
 71  
      *
 72  
      * @param option the option to search for
 73  
      * @return {@code true} if the option was detected
 74  
      * @see #has(OptionSpec)
 75  
      */
 76  
     public boolean has( String option ) {
 77  394
         return detectedOptions.containsKey( option );
 78  
     }
 79  
 
 80  
     /**
 81  
      * Tells whether the given option was detected.
 82  
      *
 83  
      * <p>This method recognizes only instances of options returned from the fluent interface methods.</p>
 84  
      *
 85  
      * <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[])} default argument value}
 86  
      * for an option does not cause this method to return {@code true} if the option was not detected on the command
 87  
      * line.</p>
 88  
      *
 89  
      * @param option the option to search for
 90  
      * @return {@code true} if the option was detected
 91  
      * @see #has(String)
 92  
      */
 93  
     public boolean has( OptionSpec<?> option ) {
 94  123
         return optionsToArguments.containsKey( option );
 95  
     }
 96  
 
 97  
     /**
 98  
      * Tells whether there are any arguments associated with the given option.
 99  
      *
 100  
      * @param option the option to search for
 101  
      * @return {@code true} if the option was detected and at least one argument was detected for the option
 102  
      * @see #hasArgument(OptionSpec)
 103  
      */
 104  
     public boolean hasArgument( String option ) {
 105  27
         AbstractOptionSpec<?> spec = detectedOptions.get( option );
 106  27
         return spec != null && hasArgument( spec );
 107  
     }
 108  
 
 109  
     /**
 110  
      * Tells whether there are any arguments associated with the given option.
 111  
      *
 112  
      * <p>This method recognizes only instances of options returned from the fluent interface methods.</p>
 113  
      *
 114  
      * <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value}
 115  
      * for an option does not cause this method to return {@code true} if the option was not detected on the command
 116  
      * line, or if the option can take an optional argument but did not have one on the command line.</p>
 117  
      *
 118  
      * @param option the option to search for
 119  
      * @return {@code true} if the option was detected and at least one argument was detected for the option
 120  
      * @throws NullPointerException if {@code option} is {@code null}
 121  
      * @see #hasArgument(String)
 122  
      */
 123  
     public boolean hasArgument( OptionSpec<?> option ) {
 124  38
         ensureNotNull( option );
 125  
 
 126  38
         List<String> values = optionsToArguments.get( option );
 127  38
         return values != null && !values.isEmpty();
 128  
     }
 129  
 
 130  
     /**
 131  
      * Gives the argument associated with the given option.  If the option was given an argument type, the argument
 132  
      * will take on that type; otherwise, it will be a {@link String}.
 133  
      *
 134  
      * <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value}
 135  
      * for an option will cause this method to return that default value even if the option was not detected on the
 136  
      * command line, or if the option can take an optional argument but did not have one on the command line.</p>
 137  
      *
 138  
      * @param option the option to search for
 139  
      * @return the argument of the given option; {@code null} if no argument is present, or that option was not
 140  
      * detected
 141  
      * @throws NullPointerException if {@code option} is {@code null}
 142  
      * @throws OptionException if more than one argument was detected for the option
 143  
      */
 144  
     public Object valueOf( String option ) {
 145  75
         ensureNotNull( option );
 146  
 
 147  74
         AbstractOptionSpec<?> spec = detectedOptions.get( option );
 148  74
         if ( spec == null ) {
 149  3
             List<?> defaults = defaultValuesFor( option );
 150  3
             return defaults.isEmpty() ? null : defaults.get( 0 );
 151  
         }
 152  
 
 153  71
         return valueOf( spec );
 154  
     }
 155  
 
 156  
     /**
 157  
      * Gives the argument associated with the given option.
 158  
      *
 159  
      * <p>This method recognizes only instances of options returned from the fluent interface methods.</p>
 160  
      *
 161  
      * @param <V> represents the type of the arguments the given option accepts
 162  
      * @param option the option to search for
 163  
      * @return the argument of the given option; {@code null} if no argument is present, or that option was not
 164  
      * detected
 165  
      * @throws OptionException if more than one argument was detected for the option
 166  
      * @throws NullPointerException if {@code option} is {@code null}
 167  
      * @throws ClassCastException if the arguments of this option are not of the expected type
 168  
      */
 169  
     public <V> V valueOf( OptionSpec<V> option ) {
 170  102
         ensureNotNull( option );
 171  
 
 172  101
         List<V> values = valuesOf( option );
 173  98
         switch ( values.size() ) {
 174  
             case 0:
 175  19
                 return null;
 176  
             case 1:
 177  77
                 return values.get( 0 );
 178  
             default:
 179  2
                 throw new MultipleArgumentsForOptionException( option );
 180  
         }
 181  
     }
 182  
 
 183  
     /**
 184  
      * <p>Gives any arguments associated with the given option.  If the option was given an argument type, the
 185  
      * arguments will take on that type; otherwise, they will be {@link String}s.</p>
 186  
      *
 187  
      * @param option the option to search for
 188  
      * @return the arguments associated with the option, as a list of objects of the type given to the arguments; an
 189  
      * empty list if no such arguments are present, or if the option was not detected
 190  
      * @throws NullPointerException if {@code option} is {@code null}
 191  
      */
 192  
     public List<?> valuesOf( String option ) {
 193  196
         ensureNotNull( option );
 194  
 
 195  195
         AbstractOptionSpec<?> spec = detectedOptions.get( option );
 196  195
         return spec == null ? defaultValuesFor( option ) : valuesOf( spec );
 197  
     }
 198  
 
 199  
     /**
 200  
      * <p>Gives any arguments associated with the given option.  If the option was given an argument type, the
 201  
      * arguments will take on that type; otherwise, they will be {@link String}s.</p>
 202  
      *
 203  
      * <p>This method recognizes only instances of options returned from the fluent interface methods.</p>
 204  
      *
 205  
      * @param <V> represents the type of the arguments the given option accepts
 206  
      * @param option the option to search for
 207  
      * @return the arguments associated with the option; an empty list if no such arguments are present, or if the
 208  
      * option was not detected
 209  
      * @throws NullPointerException if {@code option} is {@code null}
 210  
      * @throws OptionException if there is a problem converting the option's arguments to the desired type; for
 211  
      * example, if the type does not implement a correct conversion constructor or method
 212  
      */
 213  
     public <V> List<V> valuesOf( OptionSpec<V> option ) {
 214  539
         ensureNotNull( option );
 215  
 
 216  538
         List<String> values = optionsToArguments.get( option );
 217  538
         if ( values == null || values.isEmpty() )
 218  164
             return defaultValueFor( option );
 219  
 
 220  374
         AbstractOptionSpec<V> spec = (AbstractOptionSpec<V>) option;
 221  374
         List<V> convertedValues = new ArrayList<V>();
 222  374
         for ( String each : values )
 223  634
             convertedValues.add( spec.convert( each ) );
 224  
 
 225  371
         return unmodifiableList( convertedValues );
 226  
     }
 227  
 
 228  
     /**
 229  
      * Gives the set of options that were detected, in the form of {@linkplain OptionSpec}s, in the order in which the
 230  
      * options were found on the command line.
 231  
      *
 232  
      * @return the set of detected command line options
 233  
      */
 234  
     public List<OptionSpec<?>> specs() {
 235  14
         List<OptionSpec<?>> specs = detectedSpecs;
 236  14
         specs.removeAll( singletonList( detectedOptions.get( NonOptionArgumentSpec.NAME ) ) );
 237  
 
 238  14
         return unmodifiableList( specs );
 239  
     }
 240  
 
 241  
     /**
 242  
      * Gives all declared options as a map of string to {@linkplain OptionSpec}.
 243  
      *
 244  
      * @return the declared options as a map
 245  
      */
 246  
     public Map<OptionSpec<?>, List<?>> asMap() {
 247  2
         Map<OptionSpec<?>, List<?>> map = new HashMap<OptionSpec<?>, List<?>>();
 248  2
         for ( AbstractOptionSpec<?> spec : recognizedSpecs.values() )
 249  28
             if ( !spec.representsNonOptions() )
 250  12
                 map.put( spec, valuesOf( spec ) );
 251  2
         return unmodifiableMap( map );
 252  
     }
 253  
 
 254  
     /**
 255  
      * @return the detected non-option arguments
 256  
      */
 257  
     public List<?> nonOptionArguments() {
 258  119
         return unmodifiableList( valuesOf( detectedOptions.get( NonOptionArgumentSpec.NAME ) ) );
 259  
     }
 260  
 
 261  
     void add( AbstractOptionSpec<?> spec ) {
 262  454
         addWithArgument( spec, null );
 263  454
     }
 264  
 
 265  
     void addWithArgument( AbstractOptionSpec<?> spec, String argument ) {
 266  1082
         detectedSpecs.add( spec );
 267  
 
 268  1082
         for ( String each : spec.options() )
 269  1135
             detectedOptions.put( each, spec );
 270  
 
 271  1082
         List<String> optionArguments = optionsToArguments.get( spec );
 272  
 
 273  1082
         if ( optionArguments == null ) {
 274  770
             optionArguments = new ArrayList<String>();
 275  770
             optionsToArguments.put( spec, optionArguments );
 276  
         }
 277  
 
 278  1082
         if ( argument != null )
 279  628
             optionArguments.add( argument );
 280  1082
     }
 281  
 
 282  
     @Override
 283  
     public boolean equals( Object that ) {
 284  512
         if ( this == that )
 285  84
             return true;
 286  
 
 287  428
         if ( that == null || !getClass().equals( that.getClass() ) )
 288  174
             return false;
 289  
 
 290  254
         OptionSet other = (OptionSet) that;
 291  254
         Map<AbstractOptionSpec<?>, List<String>> thisOptionsToArguments =
 292  
             new HashMap<AbstractOptionSpec<?>, List<String>>( optionsToArguments );
 293  254
         Map<AbstractOptionSpec<?>, List<String>> otherOptionsToArguments =
 294  
             new HashMap<AbstractOptionSpec<?>, List<String>>( other.optionsToArguments );
 295  254
         return detectedOptions.equals( other.detectedOptions )
 296  
             && thisOptionsToArguments.equals( otherOptionsToArguments );
 297  
     }
 298  
 
 299  
     @Override
 300  
     public int hashCode() {
 301  90
         Map<AbstractOptionSpec<?>, List<String>> thisOptionsToArguments =
 302  
             new HashMap<AbstractOptionSpec<?>, List<String>>( optionsToArguments );
 303  90
         return detectedOptions.hashCode() ^ thisOptionsToArguments.hashCode();
 304  
     }
 305  
 
 306  
     @SuppressWarnings( "unchecked" )
 307  
     private <V> List<V> defaultValuesFor( String option ) {
 308  175
         if ( defaultValues.containsKey( option ) )
 309  171
             return (List<V>) defaultValues.get( option );
 310  
 
 311  4
         return emptyList();
 312  
     }
 313  
 
 314  
     private <V> List<V> defaultValueFor( OptionSpec<V> option ) {
 315  164
         return defaultValuesFor( option.options().iterator().next() );
 316  
     }
 317  
 
 318  
     private static Map<String, List<?>> defaultValues( Map<String, AbstractOptionSpec<?>> recognizedSpecs ) {
 319  432
         Map<String, List<?>> defaults = new HashMap<String, List<?>>();
 320  432
         for ( Map.Entry<String, AbstractOptionSpec<?>> each : recognizedSpecs.entrySet() )
 321  961
             defaults.put( each.getKey(), each.getValue().defaultValues() );
 322  432
         return defaults;
 323  
     }
 324  
 }