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-2014 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
56package jdk.internal.joptsimple;
57
58import java.util.ArrayList;
59import java.util.Collection;
60import java.util.List;
61import java.util.StringTokenizer;
62
63import static java.util.Collections.*;
64
65import static jdk.internal.joptsimple.internal.Objects.*;
66import static jdk.internal.joptsimple.internal.Reflection.*;
67import static jdk.internal.joptsimple.internal.Strings.*;
68
69/**
70 * <p>Specification of an option that accepts an argument.</p>
71 *
72 * <p>Instances are returned from {@link OptionSpecBuilder} methods to allow the formation of parser directives as
73 * sentences in a "fluent interface" language. For example:</p>
74 *
75 * <pre>
76 *   <code>
77 *   OptionParser parser = new OptionParser();
78 *   parser.accepts( "c" ).withRequiredArg().<strong>ofType( Integer.class )</strong>;
79 *   </code>
80 * </pre>
81 *
82 * <p>If no methods are invoked on an instance of this class, then that instance's option will treat its argument as
83 * a {@link String}.</p>
84 *
85 * @param <V> represents the type of the arguments this option accepts
86 * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
87 */
88public abstract class ArgumentAcceptingOptionSpec<V> extends AbstractOptionSpec<V> {
89    private static final char NIL_VALUE_SEPARATOR = '\u0000';
90
91    private boolean optionRequired;
92    private final boolean argumentRequired;
93    private ValueConverter<V> converter;
94    private String argumentDescription = "";
95    private String valueSeparator = String.valueOf( NIL_VALUE_SEPARATOR );
96    private final List<V> defaultValues = new ArrayList<V>();
97
98    ArgumentAcceptingOptionSpec( String option, boolean argumentRequired ) {
99        super( option );
100
101        this.argumentRequired = argumentRequired;
102    }
103
104    ArgumentAcceptingOptionSpec( Collection<String> options, boolean argumentRequired, String description ) {
105        super( options, description );
106
107        this.argumentRequired = argumentRequired;
108    }
109
110    /**
111     * <p>Specifies a type to which arguments of this spec's option are to be converted.</p>
112     *
113     * <p>JOpt Simple accepts types that have either:</p>
114     *
115     * <ol>
116     *   <li>a public static method called {@code valueOf} which accepts a single argument of type {@link String}
117     *   and whose return type is the same as the class on which the method is declared.  The {@code java.lang}
118     *   primitive wrapper classes have such methods.</li>
119     *
120     *   <li>a public constructor which accepts a single argument of type {@link String}.</li>
121     * </ol>
122     *
123     * <p>This class converts arguments using those methods in that order; that is, {@code valueOf} would be invoked
124     * before a one-{@link String}-arg constructor would.</p>
125     *
126     * <p>Invoking this method will trump any previous calls to this method or to
127     * {@link #withValuesConvertedBy(ValueConverter)}.</p>
128     *
129     * @param <T> represents the runtime class of the desired option argument type
130     * @param argumentType desired type of arguments to this spec's option
131     * @return self, so that the caller can add clauses to the fluent interface sentence
132     * @throws NullPointerException if the type is {@code null}
133     * @throws IllegalArgumentException if the type does not have the standard conversion methods
134     */
135    public final <T> ArgumentAcceptingOptionSpec<T> ofType( Class<T> argumentType ) {
136        return withValuesConvertedBy( findConverter( argumentType ) );
137    }
138
139    /**
140     * <p>Specifies a converter to use to translate arguments of this spec's option into Java objects.  This is useful
141     * when converting to types that do not have the requisite factory method or constructor for
142     * {@link #ofType(Class)}.</p>
143     *
144     * <p>Invoking this method will trump any previous calls to this method or to {@link #ofType(Class)}.
145     *
146     * @param <T> represents the runtime class of the desired option argument type
147     * @param aConverter the converter to use
148     * @return self, so that the caller can add clauses to the fluent interface sentence
149     * @throws NullPointerException if the converter is {@code null}
150     */
151    @SuppressWarnings( "unchecked" )
152    public final <T> ArgumentAcceptingOptionSpec<T> withValuesConvertedBy( ValueConverter<T> aConverter ) {
153        if ( aConverter == null )
154            throw new NullPointerException( "illegal null converter" );
155
156        converter = (ValueConverter<V>) aConverter;
157        return (ArgumentAcceptingOptionSpec<T>) this;
158    }
159
160    /**
161     * <p>Specifies a description for the argument of the option that this spec represents.  This description is used
162     * when generating help information about the parser.</p>
163     *
164     * @param description describes the nature of the argument of this spec's option
165     * @return self, so that the caller can add clauses to the fluent interface sentence
166     */
167    public final ArgumentAcceptingOptionSpec<V> describedAs( String description ) {
168        argumentDescription = description;
169        return this;
170    }
171
172    /**
173     * <p>Specifies a value separator for the argument of the option that this spec represents.  This allows a single
174     * option argument to represent multiple values for the option.  For example:</p>
175     *
176     * <pre>
177     *   <code>
178     *   parser.accepts( "z" ).withRequiredArg()
179     *       .<strong>withValuesSeparatedBy( ',' )</strong>;
180     *   OptionSet options = parser.parse( new String[] { "-z", "foo,bar,baz", "-z",
181     *       "fizz", "-z", "buzz" } );
182     *   </code>
183     * </pre>
184     *
185     * <p>Then {@code options.valuesOf( "z" )} would yield the list {@code [foo, bar, baz, fizz, buzz]}.</p>
186     *
187     * <p>You cannot use Unicode U+0000 as the separator.</p>
188     *
189     * @param separator a character separator
190     * @return self, so that the caller can add clauses to the fluent interface sentence
191     * @throws IllegalArgumentException if the separator is Unicode U+0000
192     */
193    public final ArgumentAcceptingOptionSpec<V> withValuesSeparatedBy( char separator ) {
194        if ( separator == NIL_VALUE_SEPARATOR )
195            throw new IllegalArgumentException( "cannot use U+0000 as separator" );
196
197        valueSeparator = String.valueOf( separator );
198        return this;
199    }
200
201    /**
202     * <p>Specifies a value separator for the argument of the option that this spec represents.  This allows a single
203     * option argument to represent multiple values for the option.  For example:</p>
204     *
205     * <pre>
206     *   <code>
207     *   parser.accepts( "z" ).withRequiredArg()
208     *       .<strong>withValuesSeparatedBy( ":::" )</strong>;
209     *   OptionSet options = parser.parse( new String[] { "-z", "foo:::bar:::baz", "-z",
210     *       "fizz", "-z", "buzz" } );
211     *   </code>
212     * </pre>
213     *
214     * <p>Then {@code options.valuesOf( "z" )} would yield the list {@code [foo, bar, baz, fizz, buzz]}.</p>
215     *
216     * <p>You cannot use Unicode U+0000 in the separator.</p>
217     *
218     * @param separator a string separator
219     * @return self, so that the caller can add clauses to the fluent interface sentence
220     * @throws IllegalArgumentException if the separator contains Unicode U+0000
221     */
222    public final ArgumentAcceptingOptionSpec<V> withValuesSeparatedBy( String separator ) {
223        if ( separator.indexOf( NIL_VALUE_SEPARATOR ) != -1 )
224            throw new IllegalArgumentException( "cannot use U+0000 in separator" );
225
226        valueSeparator = separator;
227        return this;
228    }
229
230    /**
231     * Specifies a set of default values for the argument of the option that this spec represents.
232     *
233     * @param value the first in the set of default argument values for this spec's option
234     * @param values the (optional) remainder of the set of default argument values for this spec's option
235     * @return self, so that the caller can add clauses to the fluent interface sentence
236     * @throws NullPointerException if {@code value}, {@code values}, or any elements of {@code values} are
237     * {@code null}
238     */
239    @SuppressWarnings("unchecked")
240    public ArgumentAcceptingOptionSpec<V> defaultsTo( V value, V... values ) {
241        addDefaultValue( value );
242        defaultsTo( values );
243
244        return this;
245    }
246
247    /**
248     * Specifies a set of default values for the argument of the option that this spec represents.
249     *
250     * @param values the set of default argument values for this spec's option
251     * @return self, so that the caller can add clauses to the fluent interface sentence
252     * @throws NullPointerException if {@code values} or any elements of {@code values} are {@code null}
253     */
254    public ArgumentAcceptingOptionSpec<V> defaultsTo( V[] values ) {
255        for ( V each : values )
256            addDefaultValue( each );
257
258        return this;
259    }
260
261    /**
262     * Marks this option as required. An {@link OptionException} will be thrown when
263     * {@link OptionParser#parse(java.lang.String...)} is called, if an option is marked as required and not specified
264     * on the command line.
265     *
266     * @return self, so that the caller can add clauses to the fluent interface sentence
267     */
268    public ArgumentAcceptingOptionSpec<V> required() {
269        optionRequired = true;
270        return this;
271    }
272
273    public boolean isRequired() {
274        return optionRequired;
275    }
276
277    private void addDefaultValue( V value ) {
278        ensureNotNull( value );
279        defaultValues.add( value );
280    }
281
282    @Override
283    final void handleOption( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions,
284        String detectedArgument ) {
285
286        if ( isNullOrEmpty( detectedArgument ) )
287            detectOptionArgument( parser, arguments, detectedOptions );
288        else
289            addArguments( detectedOptions, detectedArgument );
290    }
291
292    protected void addArguments( OptionSet detectedOptions, String detectedArgument ) {
293        StringTokenizer lexer = new StringTokenizer( detectedArgument, valueSeparator );
294        if ( !lexer.hasMoreTokens() )
295            detectedOptions.addWithArgument( this, detectedArgument );
296        else {
297            while ( lexer.hasMoreTokens() )
298                detectedOptions.addWithArgument( this, lexer.nextToken() );
299        }
300    }
301
302    protected abstract void detectOptionArgument( OptionParser parser, ArgumentList arguments,
303        OptionSet detectedOptions );
304
305    @Override
306    protected final V convert( String argument ) {
307        return convertWith( converter, argument );
308    }
309
310    protected boolean canConvertArgument( String argument ) {
311        StringTokenizer lexer = new StringTokenizer( argument, valueSeparator );
312
313        try {
314            while ( lexer.hasMoreTokens() )
315                convert( lexer.nextToken() );
316            return true;
317        }
318        catch ( OptionException ignored ) {
319            return false;
320        }
321    }
322
323    protected boolean isArgumentOfNumberType() {
324        return converter != null && Number.class.isAssignableFrom( converter.valueType() );
325    }
326
327    public boolean acceptsArguments() {
328        return true;
329    }
330
331    public boolean requiresArgument() {
332        return argumentRequired;
333    }
334
335    public String argumentDescription() {
336        return argumentDescription;
337    }
338
339    public String argumentTypeIndicator() {
340        return argumentTypeIndicatorFrom( converter );
341    }
342
343    public List<V> defaultValues() {
344        return unmodifiableList( defaultValues );
345    }
346
347    @Override
348    public boolean equals( Object that ) {
349        if ( !super.equals( that ) )
350            return false;
351
352        ArgumentAcceptingOptionSpec<?> other = (ArgumentAcceptingOptionSpec<?>) that;
353        return requiresArgument() == other.requiresArgument();
354    }
355
356    @Override
357    public int hashCode() {
358        return super.hashCode() ^ ( argumentRequired ? 0 : 1 );
359    }
360}
361