Path: blob/master/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSet.java
40948 views
/*1* Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/*26* This file is available under and governed by the GNU General Public27* License version 2 only, as published by the Free Software Foundation.28* However, the following notice accompanied the original version of this29* file:30*31* The MIT License32*33* Copyright (c) 2004-2015 Paul R. Holser, Jr.34*35* Permission is hereby granted, free of charge, to any person obtaining36* a copy of this software and associated documentation files (the37* "Software"), to deal in the Software without restriction, including38* without limitation the rights to use, copy, modify, merge, publish,39* distribute, sublicense, and/or sell copies of the Software, and to40* permit persons to whom the Software is furnished to do so, subject to41* the following conditions:42*43* The above copyright notice and this permission notice shall be44* included in all copies or substantial portions of the Software.45*46* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,47* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF48* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND49* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE50* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION51* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION52* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.53*/5455package jdk.internal.joptsimple;5657import java.util.ArrayList;58import java.util.HashMap;59import java.util.IdentityHashMap;60import java.util.List;61import java.util.Map;6263import static java.util.Collections.*;64import static java.util.Objects.*;6566/**67* Representation of a group of detected command line options, their arguments, and non-option arguments.68*69* @author <a href="mailto:[email protected]">Paul Holser</a>70*/71public class OptionSet {72private final List<OptionSpec<?>> detectedSpecs;73private final Map<String, AbstractOptionSpec<?>> detectedOptions;74private final Map<AbstractOptionSpec<?>, List<String>> optionsToArguments;75private final Map<String, AbstractOptionSpec<?>> recognizedSpecs;76private final Map<String, List<?>> defaultValues;7778/*79* Package-private because clients don't create these.80*/81OptionSet( Map<String, AbstractOptionSpec<?>> recognizedSpecs ) {82detectedSpecs = new ArrayList<>();83detectedOptions = new HashMap<>();84optionsToArguments = new IdentityHashMap<>();85defaultValues = defaultValues( recognizedSpecs );86this.recognizedSpecs = recognizedSpecs;87}8889/**90* Tells whether any options were detected.91*92* @return {@code true} if any options were detected93*/94public boolean hasOptions() {95return !( detectedOptions.size() == 1 && detectedOptions.values().iterator().next().representsNonOptions() );96}9798/**99* Tells whether the given option was detected.100*101* @param option the option to search for102* @return {@code true} if the option was detected103* @see #has(OptionSpec)104*/105public boolean has( String option ) {106return detectedOptions.containsKey( option );107}108109/**110* Tells whether the given option was detected.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 command116* line.</p>117*118* @param option the option to search for119* @return {@code true} if the option was detected120* @see #has(String)121*/122public boolean has( OptionSpec<?> option ) {123return optionsToArguments.containsKey( option );124}125126/**127* Tells whether there are any arguments associated with the given option.128*129* @param option the option to search for130* @return {@code true} if the option was detected and at least one argument was detected for the option131* @see #hasArgument(OptionSpec)132*/133public boolean hasArgument( String option ) {134AbstractOptionSpec<?> spec = detectedOptions.get( option );135return spec != null && hasArgument( spec );136}137138/**139* Tells whether there are any arguments associated with the given option.140*141* <p>This method recognizes only instances of options returned from the fluent interface methods.</p>142*143* <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value}144* for an option does not cause this method to return {@code true} if the option was not detected on the command145* line, or if the option can take an optional argument but did not have one on the command line.</p>146*147* @param option the option to search for148* @return {@code true} if the option was detected and at least one argument was detected for the option149* @throws NullPointerException if {@code option} is {@code null}150* @see #hasArgument(String)151*/152public boolean hasArgument( OptionSpec<?> option ) {153requireNonNull( option );154155List<String> values = optionsToArguments.get( option );156return values != null && !values.isEmpty();157}158159/**160* Gives the argument associated with the given option. If the option was given an argument type, the argument161* will take on that type; otherwise, it will be a {@link String}.162*163* <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value}164* for an option will cause this method to return that default value even if the option was not detected on the165* command line, or if the option can take an optional argument but did not have one on the command line.</p>166*167* @param option the option to search for168* @return the argument of the given option; {@code null} if no argument is present, or that option was not169* detected170* @throws NullPointerException if {@code option} is {@code null}171* @throws OptionException if more than one argument was detected for the option172*/173public Object valueOf( String option ) {174requireNonNull( option );175176AbstractOptionSpec<?> spec = detectedOptions.get( option );177if ( spec == null ) {178List<?> defaults = defaultValuesFor( option );179return defaults.isEmpty() ? null : defaults.get( 0 );180}181182return valueOf( spec );183}184185/**186* Gives the argument associated with the given option.187*188* <p>This method recognizes only instances of options returned from the fluent interface methods.</p>189*190* @param <V> represents the type of the arguments the given option accepts191* @param option the option to search for192* @return the argument of the given option; {@code null} if no argument is present, or that option was not193* detected194* @throws OptionException if more than one argument was detected for the option195* @throws NullPointerException if {@code option} is {@code null}196* @throws ClassCastException if the arguments of this option are not of the expected type197*/198public <V> V valueOf( OptionSpec<V> option ) {199requireNonNull( option );200201List<V> values = valuesOf( option );202switch ( values.size() ) {203case 0:204return null;205case 1:206return values.get( 0 );207default:208throw new MultipleArgumentsForOptionException( option );209}210}211212/**213* <p>Gives any arguments associated with the given option. If the option was given an argument type, the214* arguments will take on that type; otherwise, they will be {@link String}s.</p>215*216* @param option the option to search for217* @return the arguments associated with the option, as a list of objects of the type given to the arguments; an218* empty list if no such arguments are present, or if the option was not detected219* @throws NullPointerException if {@code option} is {@code null}220*/221public List<?> valuesOf( String option ) {222requireNonNull( option );223224AbstractOptionSpec<?> spec = detectedOptions.get( option );225return spec == null ? defaultValuesFor( option ) : valuesOf( spec );226}227228/**229* <p>Gives any arguments associated with the given option. If the option was given an argument type, the230* arguments will take on that type; otherwise, they will be {@link String}s.</p>231*232* <p>This method recognizes only instances of options returned from the fluent interface methods.</p>233*234* @param <V> represents the type of the arguments the given option accepts235* @param option the option to search for236* @return the arguments associated with the option; an empty list if no such arguments are present, or if the237* option was not detected238* @throws NullPointerException if {@code option} is {@code null}239* @throws OptionException if there is a problem converting the option's arguments to the desired type; for240* example, if the type does not implement a correct conversion constructor or method241*/242public <V> List<V> valuesOf( OptionSpec<V> option ) {243requireNonNull( option );244245List<String> values = optionsToArguments.get( option );246if ( values == null || values.isEmpty() )247return defaultValueFor( option );248249AbstractOptionSpec<V> spec = (AbstractOptionSpec<V>) option;250List<V> convertedValues = new ArrayList<>();251for ( String each : values )252convertedValues.add( spec.convert( each ) );253254return unmodifiableList( convertedValues );255}256257/**258* Gives the set of options that were detected, in the form of {@linkplain OptionSpec}s, in the order in which the259* options were found on the command line.260*261* @return the set of detected command line options262*/263public List<OptionSpec<?>> specs() {264List<OptionSpec<?>> specs = detectedSpecs;265specs.removeAll( singletonList( detectedOptions.get( NonOptionArgumentSpec.NAME ) ) );266267return unmodifiableList( specs );268}269270/**271* Gives all declared options as a map of string to {@linkplain OptionSpec}.272*273* @return the declared options as a map274*/275public Map<OptionSpec<?>, List<?>> asMap() {276Map<OptionSpec<?>, List<?>> map = new HashMap<>();277278for ( AbstractOptionSpec<?> spec : recognizedSpecs.values() ) {279if ( !spec.representsNonOptions() )280map.put( spec, valuesOf( spec ) );281}282283return unmodifiableMap( map );284}285286/**287* @return the detected non-option arguments288*/289public List<?> nonOptionArguments() {290AbstractOptionSpec<?> spec = detectedOptions.get( NonOptionArgumentSpec.NAME );291return valuesOf( spec );292}293294void add( AbstractOptionSpec<?> spec ) {295addWithArgument( spec, null );296}297298void addWithArgument( AbstractOptionSpec<?> spec, String argument ) {299detectedSpecs.add( spec );300301for ( String each : spec.options() )302detectedOptions.put( each, spec );303304List<String> optionArguments = optionsToArguments.get( spec );305306if ( optionArguments == null ) {307optionArguments = new ArrayList<>();308optionsToArguments.put( spec, optionArguments );309}310311if ( argument != null )312optionArguments.add( argument );313}314315@Override316public boolean equals( Object that ) {317if ( this == that )318return true;319320if ( that == null || !getClass().equals( that.getClass() ) )321return false;322323OptionSet other = (OptionSet) that;324Map<AbstractOptionSpec<?>, List<String>> thisOptionsToArguments = new HashMap<>( optionsToArguments );325Map<AbstractOptionSpec<?>, List<String>> otherOptionsToArguments = new HashMap<>( other.optionsToArguments );326return detectedOptions.equals( other.detectedOptions )327&& thisOptionsToArguments.equals( otherOptionsToArguments );328}329330@Override331public int hashCode() {332Map<AbstractOptionSpec<?>, List<String>> thisOptionsToArguments = new HashMap<>( optionsToArguments );333return detectedOptions.hashCode() ^ thisOptionsToArguments.hashCode();334}335336@SuppressWarnings( "unchecked" )337private <V> List<V> defaultValuesFor( String option ) {338if ( defaultValues.containsKey( option ) )339return unmodifiableList( (List<V>) defaultValues.get( option ) );340341return emptyList();342}343344private <V> List<V> defaultValueFor( OptionSpec<V> option ) {345return defaultValuesFor( option.options().iterator().next() );346}347348private static Map<String, List<?>> defaultValues( Map<String, AbstractOptionSpec<?>> recognizedSpecs ) {349Map<String, List<?>> defaults = new HashMap<>();350for ( Map.Entry<String, AbstractOptionSpec<?>> each : recognizedSpecs.entrySet() )351defaults.put( each.getKey(), each.getValue().defaultValues() );352return defaults;353}354}355356357