1 /* 2 * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /* 27 * This file is available under and governed by the GNU General Public 28 * License version 2 only, as published by the Free Software Foundation. 29 * However, the following notice accompanied the original version of this 30 * file: 31 * 32 * The MIT License 33 * 34 * Copyright (c) 2004-2015 Paul R. Holser, Jr. 35 * 36 * Permission is hereby granted, free of charge, to any person obtaining 37 * a copy of this software and associated documentation files (the 38 * "Software"), to deal in the Software without restriction, including 39 * without limitation the rights to use, copy, modify, merge, publish, 40 * distribute, sublicense, and/or sell copies of the Software, and to 41 * permit persons to whom the Software is furnished to do so, subject to 42 * the following conditions: 43 * 44 * The above copyright notice and this permission notice shall be 45 * included in all copies or substantial portions of the Software. 46 * 47 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 48 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 49 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 50 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 51 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 52 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 53 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 54 */ 55 56 package jdk.internal.joptsimple; 57 58 import java.util.ArrayList; 59 import java.util.Collections; 60 import java.util.List; 61 62 /** 63 * Allows callers to specify whether a given option accepts arguments (required or optional). 64 * 65 * <p>Instances are returned from {@link OptionParser#accepts(String)} to allow the formation of parser directives as 66 * sentences in a "fluent interface" language. For example:</p> 67 * 68 * <pre><code> 69 * OptionParser parser = new OptionParser(); 70 * parser.accepts( "c" ).<strong>withRequiredArg()</strong>.ofType( Integer.class ); 71 * </code></pre> 72 * 73 * <p>If no methods are invoked on an instance of this class, then that instance's option will accept no argument.</p> 74 * 75 * <p>Note that you should not use the fluent interface clauses in a way that would defeat the typing of option 76 * arguments:</p> 77 * 78 * <pre><code> 79 * OptionParser parser = new OptionParser(); 80 * ArgumentAcceptingOptionSpec<String> optionC = 81 * parser.accepts( "c" ).withRequiredArg(); 82 * <strong>optionC.ofType( Integer.class ); // DON'T THROW AWAY THE TYPE!</strong> 83 * 84 * String value = parser.parse( "-c", "2" ).valueOf( optionC ); // ClassCastException 85 * </code></pre> 86 * 87 * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a> 88 */ 89 public class OptionSpecBuilder extends NoArgumentOptionSpec { 90 private final OptionParser parser; 91 OptionSpecBuilder( OptionParser parser, List<String> options, String description )92 OptionSpecBuilder( OptionParser parser, List<String> options, String description ) { 93 super( options, description ); 94 95 this.parser = parser; 96 attachToParser(); 97 } 98 attachToParser()99 private void attachToParser() { 100 parser.recognize( this ); 101 } 102 103 /** 104 * Informs an option parser that this builder's option requires an argument. 105 * 106 * @return a specification for the option 107 */ withRequiredArg()108 public ArgumentAcceptingOptionSpec<String> withRequiredArg() { 109 ArgumentAcceptingOptionSpec<String> newSpec = new RequiredArgumentOptionSpec<>( options(), description() ); 110 parser.recognize( newSpec ); 111 112 return newSpec; 113 } 114 115 /** 116 * Informs an option parser that this builder's option accepts an optional argument. 117 * 118 * @return a specification for the option 119 */ withOptionalArg()120 public ArgumentAcceptingOptionSpec<String> withOptionalArg() { 121 ArgumentAcceptingOptionSpec<String> newSpec = 122 new OptionalArgumentOptionSpec<>( options(), description() ); 123 parser.recognize( newSpec ); 124 125 return newSpec; 126 } 127 128 /** 129 * <p>Informs an option parser that this builder's option is required if the given option is present on the command 130 * line.</p> 131 * 132 * <p>For a given option, you <em>should not</em> mix this with {@link #requiredUnless(String, String...) 133 * requiredUnless} to avoid conflicts.</p> 134 * 135 * @param dependent an option whose presence on a command line makes this builder's option required 136 * @param otherDependents other options whose presence on a command line makes this builder's option required 137 * @return self, so that the caller can add clauses to the fluent interface sentence 138 * @throws OptionException if any of the dependent options haven't been configured in the parser yet 139 */ requiredIf( String dependent, String... otherDependents )140 public OptionSpecBuilder requiredIf( String dependent, String... otherDependents ) { 141 List<String> dependents = validatedDependents( dependent, otherDependents ); 142 for ( String each : dependents ) 143 parser.requiredIf( options(), each ); 144 145 return this; 146 } 147 148 /** 149 * <p>Informs an option parser that this builder's option is required if the given option is present on the command 150 * line.</p> 151 * 152 * <p>For a given option, you <em>should not</em> mix this with {@link #requiredUnless(OptionSpec, OptionSpec[]) 153 * requiredUnless} to avoid conflicts.</p> 154 * 155 * <p>This method recognizes only instances of options returned from the fluent interface methods.</p> 156 * 157 * @param dependent the option whose presence on a command line makes this builder's option required 158 * @param otherDependents other options whose presence on a command line makes this builder's option required 159 * @return self, so that the caller can add clauses to the fluent interface sentence 160 */ requiredIf( OptionSpec<?> dependent, OptionSpec<?>... otherDependents )161 public OptionSpecBuilder requiredIf( OptionSpec<?> dependent, OptionSpec<?>... otherDependents ) { 162 parser.requiredIf( options(), dependent ); 163 for ( OptionSpec<?> each : otherDependents ) 164 parser.requiredIf( options(), each ); 165 166 return this; 167 } 168 169 /** 170 * <p>Informs an option parser that this builder's option is required if the given option is absent on the command 171 * line.</p> 172 * 173 * <p>For a given option, you <em>should not</em> mix this with {@link #requiredIf(OptionSpec, OptionSpec[]) 174 * requiredIf} to avoid conflicts.</p> 175 * 176 * @param dependent an option whose absence on a command line makes this builder's option required 177 * @param otherDependents other options whose absence on a command line makes this builder's option required 178 * @return self, so that the caller can add clauses to the fluent interface sentence 179 * @throws OptionException if any of the dependent options haven't been configured in the parser yet 180 */ requiredUnless( String dependent, String... otherDependents )181 public OptionSpecBuilder requiredUnless( String dependent, String... otherDependents ) { 182 List<String> dependents = validatedDependents( dependent, otherDependents ); 183 for ( String each : dependents ) { 184 parser.requiredUnless( options(), each ); 185 } 186 return this; 187 } 188 189 /** 190 * <p>Informs an option parser that this builder's option is required if the given option is absent on the command 191 * line.</p> 192 * 193 * <p>For a given option, you <em>should not</em> mix this with {@link #requiredIf(OptionSpec, OptionSpec[]) 194 * requiredIf} to avoid conflicts.</p> 195 * 196 * <p>This method recognizes only instances of options returned from the fluent interface methods.</p> 197 * 198 * @param dependent the option whose absence on a command line makes this builder's option required 199 * @param otherDependents other options whose absence on a command line makes this builder's option required 200 * @return self, so that the caller can add clauses to the fluent interface sentence 201 */ requiredUnless( OptionSpec<?> dependent, OptionSpec<?>... otherDependents )202 public OptionSpecBuilder requiredUnless( OptionSpec<?> dependent, OptionSpec<?>... otherDependents ) { 203 parser.requiredUnless( options(), dependent ); 204 for ( OptionSpec<?> each : otherDependents ) 205 parser.requiredUnless( options(), each ); 206 207 return this; 208 } 209 210 /** 211 * <p>Informs an option parser that this builder's option is allowed if the given option is present on the command 212 * line.</p> 213 * 214 * <p>For a given option, you <em>should not</em> mix this with {@link #availableUnless(String, String...) 215 * availableUnless} to avoid conflicts.</p> 216 * 217 * @param dependent an option whose presence on a command line makes this builder's option allowed 218 * @param otherDependents other options whose presence on a command line makes this builder's option allowed 219 * @return self, so that the caller can add clauses to the fluent interface sentence 220 * @throws OptionException if any of the dependent options haven't been configured in the parser yet 221 */ availableIf( String dependent, String... otherDependents )222 public OptionSpecBuilder availableIf( String dependent, String... otherDependents ) { 223 List<String> dependents = validatedDependents( dependent, otherDependents ); 224 for ( String each : dependents ) 225 parser.availableIf( options(), each ); 226 227 return this; 228 } 229 230 /** 231 * <p>Informs an option parser that this builder's option is allowed if the given option is present on the command 232 * line.</p> 233 * 234 * <p>For a given option, you <em>should not</em> mix this with {@link #availableUnless(OptionSpec, OptionSpec[]) 235 * requiredUnless} to avoid conflicts.</p> 236 * 237 * <p>This method recognizes only instances of options returned from the fluent interface methods.</p> 238 * 239 * @param dependent the option whose presence on a command line makes this builder's option allowed 240 * @param otherDependents other options whose presence on a command line makes this builder's option allowed 241 * @return self, so that the caller can add clauses to the fluent interface sentence 242 */ availableIf( OptionSpec<?> dependent, OptionSpec<?>... otherDependents )243 public OptionSpecBuilder availableIf( OptionSpec<?> dependent, OptionSpec<?>... otherDependents ) { 244 parser.availableIf( options(), dependent ); 245 246 for ( OptionSpec<?> each : otherDependents ) 247 parser.availableIf( options(), each ); 248 249 return this; 250 } 251 252 /** 253 * <p>Informs an option parser that this builder's option is allowed if the given option is absent on the command 254 * line.</p> 255 * 256 * <p>For a given option, you <em>should not</em> mix this with {@link #availableIf(OptionSpec, OptionSpec[]) 257 * requiredIf} to avoid conflicts.</p> 258 * 259 * @param dependent an option whose absence on a command line makes this builder's option allowed 260 * @param otherDependents other options whose absence on a command line makes this builder's option allowed 261 * @return self, so that the caller can add clauses to the fluent interface sentence 262 * @throws OptionException if any of the dependent options haven't been configured in the parser yet 263 */ availableUnless( String dependent, String... otherDependents )264 public OptionSpecBuilder availableUnless( String dependent, String... otherDependents ) { 265 List<String> dependents = validatedDependents( dependent, otherDependents ); 266 for ( String each : dependents ) 267 parser.availableUnless( options(), each ); 268 269 return this; 270 } 271 272 /** 273 * <p>Informs an option parser that this builder's option is allowed if the given option is absent on the command 274 * line.</p> 275 * 276 * <p>For a given option, you <em>should not</em> mix this with {@link #availableIf(OptionSpec, OptionSpec[]) 277 * requiredIf} to avoid conflicts.</p> 278 * 279 * <p>This method recognizes only instances of options returned from the fluent interface methods.</p> 280 * 281 * @param dependent the option whose absence on a command line makes this builder's option allowed 282 * @param otherDependents other options whose absence on a command line makes this builder's option allowed 283 * @return self, so that the caller can add clauses to the fluent interface sentence 284 */ availableUnless( OptionSpec<?> dependent, OptionSpec<?>... otherDependents )285 public OptionSpecBuilder availableUnless( OptionSpec<?> dependent, OptionSpec<?>... otherDependents ) { 286 parser.availableUnless( options(), dependent ); 287 for ( OptionSpec<?> each : otherDependents ) 288 parser.availableUnless(options(), each); 289 290 return this; 291 } 292 validatedDependents( String dependent, String... otherDependents )293 private List<String> validatedDependents( String dependent, String... otherDependents ) { 294 List<String> dependents = new ArrayList<>(); 295 dependents.add( dependent ); 296 Collections.addAll( dependents, otherDependents ); 297 298 for ( String each : dependents ) { 299 if ( !parser.isRecognized( each ) ) 300 throw new UnconfiguredOptionException( each ); 301 } 302 303 return dependents; 304 } 305 } 306