src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionParser.java
changeset 50428 8c88df2e8a78
parent 47216 71c04702a3d5
--- a/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionParser.java	Wed Jun 06 13:06:21 2018 +0100
+++ b/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionParser.java	Wed Jun 06 15:36:29 2018 +0200
@@ -31,7 +31,7 @@
  *
  * The MIT License
  *
- * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ * Copyright (c) 2004-2015 Paul R. Holser, Jr.
  *
  * Permission is hereby granted, free of charge, to any person obtaining
  * a copy of this software and associated documentation files (the
@@ -55,18 +55,16 @@
 
 package jdk.internal.joptsimple;
 
-import jdk.internal.joptsimple.internal.AbbreviationMap;
-import jdk.internal.joptsimple.util.KeyValuePair;
-
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.io.Writer;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
+
+import jdk.internal.joptsimple.internal.AbbreviationMap;
+import jdk.internal.joptsimple.internal.SimpleOptionNameMap;
+import jdk.internal.joptsimple.internal.OptionNameMap;
+import jdk.internal.joptsimple.util.KeyValuePair;
 
 import static java.util.Collections.*;
 import static jdk.internal.joptsimple.OptionException.*;
@@ -80,57 +78,58 @@
  * <p>This parser supports short options and long options.</p>
  *
  * <ul>
- *   <li><dfn>Short options</dfn> begin with a single hyphen ("<kbd>-</kbd>") followed by a single letter or digit,
- *   or question mark ("<kbd>?</kbd>"), or dot ("<kbd>.</kbd>").</li>
+ *   <li><dfn>Short options</dfn> begin with a single hyphen ("{@code -}") followed by a single letter or digit,
+ *   or question mark ("{@code ?}"), or dot ("{@code .}"), or underscore ("{@code _}").</li>
  *
  *   <li>Short options can accept single arguments. The argument can be made required or optional. The option's
  *   argument can occur:
  *     <ul>
- *       <li>in the slot after the option, as in <kbd>-d /tmp</kbd></li>
- *       <li>right up against the option, as in <kbd>-d/tmp</kbd></li>
- *       <li>right up against the option separated by an equals sign (<kbd>"="</kbd>), as in <kbd>-d=/tmp</kbd></li>
+ *       <li>in the slot after the option, as in {@code -d /tmp}</li>
+ *       <li>right up against the option, as in {@code -d/tmp}</li>
+ *       <li>right up against the option separated by an equals sign ({@code "="}), as in {@code -d=/tmp}</li>
  *     </ul>
  *   To specify <em>n</em> arguments for an option, specify the option <em>n</em> times, once for each argument,
- *   as in <kbd>-d /tmp -d /var -d /opt</kbd>; or, when using the
+ *   as in {@code -d /tmp -d /var -d /opt}; or, when using the
  *   {@linkplain ArgumentAcceptingOptionSpec#withValuesSeparatedBy(char) "separated values"} clause of the "fluent
  *   interface" (see below), give multiple values separated by a given character as a single argument to the
  *   option.</li>
  *
- *   <li>Short options can be clustered, so that <kbd>-abc</kbd> is treated as <kbd>-a -b -c</kbd>. If a short option
+ *   <li>Short options can be clustered, so that {@code -abc} is treated as {@code -a -b -c}. If a short option
  *   in the cluster can accept an argument, the remaining characters are interpreted as the argument for that
  *   option.</li>
  *
- *   <li>An argument consisting only of two hyphens (<kbd>"--"</kbd>) signals that the remaining arguments are to be
+ *   <li>An argument consisting only of two hyphens ({@code "--"}) signals that the remaining arguments are to be
  *   treated as non-options.</li>
  *
  *   <li>An argument consisting only of a single hyphen is considered a non-option argument (though it can be an
  *   argument of an option). Many Unix programs treat single hyphens as stand-ins for the standard input or standard
  *   output streams.</li>
  *
- *   <li><dfn>Long options</dfn> begin with two hyphens (<kbd>"--"</kbd>), followed by multiple letters, digits,
+ *   <li><dfn>Long options</dfn> begin with two hyphens ({@code "--"}), followed by multiple letters, digits,
  *   hyphens, question marks, or dots. A hyphen cannot be the first character of a long option specification when
  *   configuring the parser.</li>
  *
- *   <li>You can abbreviate long options, so long as the abbreviation is unique.</li>
+ *   <li>You can abbreviate long options, so long as the abbreviation is unique. Suppress this behavior if
+ *   you wish using {@linkplain OptionParser#OptionParser(boolean) this constructor}.</li>
  *
  *   <li>Long options can accept single arguments.  The argument can be made required or optional.  The option's
  *   argument can occur:
  *     <ul>
- *       <li>in the slot after the option, as in <kbd>--directory /tmp</kbd></li>
- *       <li>right up against the option separated by an equals sign (<kbd>"="</kbd>), as in
- *       <kbd>--directory=/tmp</kbd>
+ *       <li>in the slot after the option, as in {@code --directory /tmp}</li>
+ *       <li>right up against the option separated by an equals sign ({@code "="}), as in
+ *       {@code --directory=/tmp}
  *     </ul>
  *   Specify multiple arguments for a long option in the same manner as for short options (see above).</li>
  *
- *   <li>You can use a single hyphen (<kbd>"-"</kbd>) instead of a double hyphen (<kbd>"--"</kbd>) for a long
+ *   <li>You can use a single hyphen ({@code "-"}) instead of a double hyphen ({@code "--"}) for a long
  *   option.</li>
  *
- *   <li>The option <kbd>-W</kbd> is reserved.  If you tell the parser to {@linkplain
+ *   <li>The option {@code -W} is reserved.  If you tell the parser to {@linkplain
  *   #recognizeAlternativeLongOptions(boolean) recognize alternative long options}, then it will treat, for example,
- *   <kbd>-W foo=bar</kbd> as the long option <kbd>foo</kbd> with argument <kbd>bar</kbd>, as though you had written
- *   <kbd>--foo=bar</kbd>.</li>
+ *   {@code -W foo=bar} as the long option {@code foo} with argument {@code bar}, as though you had written
+ *   {@code --foo=bar}.</li>
  *
- *   <li>You can specify <kbd>-W</kbd> as a valid short option, or use it as an abbreviation for a long option, but
+ *   <li>You can specify {@code -W} as a valid short option, or use it as an abbreviation for a long option, but
  *   {@linkplain #recognizeAlternativeLongOptions(boolean) recognizing alternative long options} will always supersede
  *   this behavior.</li>
  *
@@ -148,15 +147,15 @@
  *     parser.accepts( "2" );
  *     OptionSet options = parser.parse( "-a", "-2" );
  *   </code></pre>
- *   In this case, the option set contains <kbd>"a"</kbd> with argument <kbd>-2</kbd>, not both <kbd>"a"</kbd> and
- *   <kbd>"2"</kbd>. Swapping the elements in the <em>args</em> array gives the latter.</li>
+ *   In this case, the option set contains {@code "a"} with argument {@code -2}, not both {@code "a"} and
+ *   {@code "2"}. Swapping the elements in the <em>args</em> array gives the latter.</li>
  * </ul>
  *
  * <p>There are two ways to tell the parser what options to recognize:</p>
  *
  * <ol>
  *   <li>A "fluent interface"-style API for specifying options, available since version 2. Sentences in this fluent
- *   interface language begin with a call to {@link #accepts(String) accepts} or {@link #acceptsAll(Collection)
+ *   interface language begin with a call to {@link #accepts(String) accepts} or {@link #acceptsAll(List)
  *   acceptsAll} methods; calls on the ensuing chain of objects describe whether the options can take an argument,
  *   whether the argument is required or optional, to what type arguments of the options should be converted if any,
  *   etc. Since version 3, these calls return an instance of {@link OptionSpec}, which can subsequently be used to
@@ -169,28 +168,28 @@
  *     <ul>
  *       <li>Any letter or digit is treated as an option character.</li>
  *
- *       <li>An option character can be immediately followed by an asterisk (*) to indicate that the option is a
- *       "help" option.</li>
+ *       <li>An option character can be immediately followed by an asterisk ({@code *)} to indicate that
+ *       the option is a "help" option.</li>
  *
- *       <li>If an option character (with possible trailing asterisk) is followed by a single colon (<kbd>":"</kbd>),
+ *       <li>If an option character (with possible trailing asterisk) is followed by a single colon ({@code ":"}),
  *       then the option requires an argument.</li>
  *
- *       <li>If an option character (with possible trailing asterisk) is followed by two colons (<kbd>"::"</kbd>),
+ *       <li>If an option character (with possible trailing asterisk) is followed by two colons ({@code "::"}),
  *       then the option accepts an optional argument.</li>
  *
  *       <li>Otherwise, the option character accepts no argument.</li>
  *
- *       <li>If the option specification string begins with a plus sign (<kbd>"+"</kbd>), the parser will behave
+ *       <li>If the option specification string begins with a plus sign ({@code "+" }), the parser will behave
  *       "POSIX-ly correct".</li>
  *
- *       <li>If the option specification string contains the sequence <kbd>"W;"</kbd> (capital W followed by a
+ *       <li>If the option specification string contains the sequence {@code "W;"} (capital W followed by a
  *       semicolon), the parser will recognize the alternative form of long options.</li>
  *     </ul>
  *   </li>
  * </ol>
  *
- * <p>Each of the options in a list of options given to {@link #acceptsAll(Collection) acceptsAll} is treated as a
- * synonym of the others.  For example:
+ * <p>Each of the options in a list of options given to {@link #acceptsAll(List) acceptsAll} is treated as a
+ * synonym of the others.  For example:</p>
  *   <pre>
  *     <code>
  *     OptionParser parser = new OptionParser();
@@ -198,14 +197,14 @@
  *     OptionSet options = parser.parse( "-w" );
  *     </code>
  *   </pre>
- * In this case, <code>options.{@link OptionSet#has(String) has}</code> would answer {@code true} when given arguments
- * <kbd>"w"</kbd>, <kbd>"interactive"</kbd>, and <kbd>"confirmation"</kbd>. The {@link OptionSet} would give the same
+ * <p>In this case, <code>options.{@link OptionSet#has(String) has}</code> would answer {@code true} when given arguments
+ * {@code "w"}, {@code "interactive"}, and {@code "confirmation"}. The {@link OptionSet} would give the same
  * responses to these arguments for its other methods as well.</p>
  *
  * <p>By default, as with GNU {@code getopt()}, the parser allows intermixing of options and non-options. If, however,
  * the parser has been created to be "POSIX-ly correct", then the first argument that does not look lexically like an
  * option, and is not a required argument of a preceding option, signals the end of options. You can still bind
- * optional arguments to their options using the abutting (for short options) or <kbd>=</kbd> syntax.</p>
+ * optional arguments to their options using the abutting (for short options) or {@code =} syntax.</p>
  *
  * <p>Unlike GNU {@code getopt()}, this parser does not honor the environment variable {@code POSIXLY_CORRECT}.
  * "POSIX-ly correct" parsers are configured by either:</p>
@@ -214,16 +213,20 @@
  *   <li>using the method {@link #posixlyCorrect(boolean)}, or</li>
  *
  *   <li>using the {@linkplain #OptionParser(String) constructor} with an argument whose first character is a plus sign
- *   (<kbd>"+"</kbd>)</li>
+ *   ({@code "+"})</li>
  * </ol>
  *
  * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
  * @see <a href="http://www.gnu.org/software/libc/manual">The GNU C Library</a>
  */
 public class OptionParser implements OptionDeclarer {
-    private final AbbreviationMap<AbstractOptionSpec<?>> recognizedOptions;
-    private final Map<Collection<String>, Set<OptionSpec<?>>> requiredIf;
-    private final Map<Collection<String>, Set<OptionSpec<?>>> requiredUnless;
+    private final OptionNameMap<AbstractOptionSpec<?>> recognizedOptions;
+    private final ArrayList<AbstractOptionSpec<?>> trainingOrder;
+    private final Map<List<String>, Set<OptionSpec<?>>> requiredIf;
+    private final Map<List<String>, Set<OptionSpec<?>>> requiredUnless;
+    private final Map<List<String>, Set<OptionSpec<?>>> availableIf;
+    private final Map<List<String>, Set<OptionSpec<?>>> availableUnless;
+
     private OptionParserState state;
     private boolean posixlyCorrect;
     private boolean allowsUnrecognizedOptions;
@@ -234,11 +237,28 @@
      * behavior.
      */
     public OptionParser() {
-        recognizedOptions = new AbbreviationMap<AbstractOptionSpec<?>>();
-        requiredIf = new HashMap<Collection<String>, Set<OptionSpec<?>>>();
-        requiredUnless = new HashMap<Collection<String>, Set<OptionSpec<?>>>();
+        this(true);
+    }
+
+    /**
+     * Creates an option parser that initially recognizes no options, and does not exhibit "POSIX-ly correct"
+     * behavior.
+     *
+     * @param allowAbbreviations whether unambiguous abbreviations of long options should be recognized
+     * by the parser
+     */
+    public OptionParser( boolean allowAbbreviations ) {
+        trainingOrder = new ArrayList<>();
+        requiredIf = new HashMap<>();
+        requiredUnless = new HashMap<>();
+        availableIf = new HashMap<>();
+        availableUnless = new HashMap<>();
         state = moreOptions( false );
 
+        recognizedOptions = allowAbbreviations
+            ? new AbbreviationMap<AbstractOptionSpec<?>>()
+            : new SimpleOptionNameMap<AbstractOptionSpec<?>>();
+
         recognize( new NonOptionArgumentSpec<String>() );
     }
 
@@ -266,11 +286,11 @@
         return acceptsAll( singletonList( option ), description );
     }
 
-    public OptionSpecBuilder acceptsAll( Collection<String> options ) {
+    public OptionSpecBuilder acceptsAll( List<String> options ) {
         return acceptsAll( options, "" );
     }
 
-    public OptionSpecBuilder acceptsAll( Collection<String> options, String description ) {
+    public OptionSpecBuilder acceptsAll( List<String> options, String description ) {
         if ( options.isEmpty() )
             throw new IllegalArgumentException( "need at least one option" );
 
@@ -280,7 +300,7 @@
     }
 
     public NonOptionArgumentSpec<String> nonOptions() {
-        NonOptionArgumentSpec<String> spec = new NonOptionArgumentSpec<String>();
+        NonOptionArgumentSpec<String> spec = new NonOptionArgumentSpec<>();
 
         recognize( spec );
 
@@ -288,7 +308,7 @@
     }
 
     public NonOptionArgumentSpec<String> nonOptions( String description ) {
-        NonOptionArgumentSpec<String> spec = new NonOptionArgumentSpec<String>( description );
+        NonOptionArgumentSpec<String> spec = new NonOptionArgumentSpec<>( description );
 
         recognize( spec );
 
@@ -321,6 +341,7 @@
 
     void recognize( AbstractOptionSpec<?> spec ) {
         recognizedOptions.putAll( spec.options(), spec );
+        trainingOrder.add( spec );
     }
 
     /**
@@ -348,7 +369,7 @@
      * @see #printHelpOn(OutputStream)
      */
     public void printHelpOn( Writer sink ) throws IOException {
-        sink.write( helpFormatter.format( recognizedOptions.toJavaUtilMap() ) );
+        sink.write( helpFormatter.format( _recognizedOptions() ) );
         sink.flush();
     }
 
@@ -366,15 +387,29 @@
     }
 
     /**
-     * Retrieves all the options which have been configured for the parser.
+     * Retrieves all options-spec pairings which have been configured for the parser in the same order as declared
+     * during training. Option flags for specs are alphabetized by {@link OptionSpec#options()}; only the order of the
+     * specs is preserved.
      *
-     * @return a {@link Map} containing all the configured options and their corresponding {@link OptionSpec}
+     * (Note: prior to 4.7 the order was alphabetical across all options regardless of spec.)
+     *
+     * @return a map containing all the configured options and their corresponding {@link OptionSpec}
+     * @since 4.6
      */
     public Map<String, OptionSpec<?>> recognizedOptions() {
-        return new HashMap<String, OptionSpec<?>>( recognizedOptions.toJavaUtilMap() );
+        return new LinkedHashMap<String, OptionSpec<?>>( _recognizedOptions() );
     }
 
-    /**
+    private Map<String, AbstractOptionSpec<?>> _recognizedOptions() {
+        Map<String, AbstractOptionSpec<?>> options = new LinkedHashMap<>();
+        for ( AbstractOptionSpec<?> spec : trainingOrder ) {
+            for ( String option : spec.options() )
+                options.put( option, spec );
+        }
+        return options;
+    }
+
+   /**
      * Parses the given command line arguments according to the option specifications given to the parser.
      *
      * @param arguments arguments to parse
@@ -393,43 +428,87 @@
         reset();
 
         ensureRequiredOptions( detected );
+        ensureAllowedOptions( detected );
 
         return detected;
     }
 
+    /**
+     * Mandates mutual exclusiveness for the options built by the specified builders.
+     *
+     * @param specs descriptors for options that should be mutually exclusive on a command line.
+     * @throws NullPointerException if {@code specs} is {@code null}
+     */
+    public void mutuallyExclusive( OptionSpecBuilder... specs ) {
+        for ( int i = 0; i < specs.length; i++ ) {
+            for ( int j = 0; j < specs.length; j++ ) {
+                if ( i != j )
+                    specs[i].availableUnless( specs[j] );
+            }
+        }
+    }
+
     private void ensureRequiredOptions( OptionSet options ) {
-        Collection<String> missingRequiredOptions = missingRequiredOptions( options );
+        List<AbstractOptionSpec<?>> missingRequiredOptions = missingRequiredOptions(options);
         boolean helpOptionPresent = isHelpOptionPresent( options );
 
         if ( !missingRequiredOptions.isEmpty() && !helpOptionPresent )
-            throw new MissingRequiredOptionException( missingRequiredOptions );
+            throw new MissingRequiredOptionsException( missingRequiredOptions );
     }
 
-    private Collection<String> missingRequiredOptions( OptionSet options ) {
-        Collection<String> missingRequiredOptions = new HashSet<String>();
+    private void ensureAllowedOptions( OptionSet options ) {
+        List<AbstractOptionSpec<?>> forbiddenOptions = unavailableOptions( options );
+        boolean helpOptionPresent = isHelpOptionPresent( options );
+
+        if ( !forbiddenOptions.isEmpty() && !helpOptionPresent )
+            throw new UnavailableOptionException( forbiddenOptions );
+    }
+
+    private List<AbstractOptionSpec<?>> missingRequiredOptions( OptionSet options ) {
+        List<AbstractOptionSpec<?>> missingRequiredOptions = new ArrayList<>();
 
         for ( AbstractOptionSpec<?> each : recognizedOptions.toJavaUtilMap().values() ) {
             if ( each.isRequired() && !options.has( each ) )
-                missingRequiredOptions.addAll( each.options() );
+                missingRequiredOptions.add(each);
+        }
+
+        for ( Map.Entry<List<String>, Set<OptionSpec<?>>> each : requiredIf.entrySet() ) {
+            AbstractOptionSpec<?> required = specFor( each.getKey().iterator().next() );
+
+            if ( optionsHasAnyOf( options, each.getValue() ) && !options.has( required ) )
+                missingRequiredOptions.add( required );
         }
 
-        for ( Map.Entry<Collection<String>, Set<OptionSpec<?>>> eachEntry : requiredIf.entrySet() ) {
-            AbstractOptionSpec<?> required = specFor( eachEntry.getKey().iterator().next() );
+        for ( Map.Entry<List<String>, Set<OptionSpec<?>>> each : requiredUnless.entrySet() ) {
+            AbstractOptionSpec<?> required = specFor(each.getKey().iterator().next());
+
+            if ( !optionsHasAnyOf( options, each.getValue() ) && !options.has( required ) )
+                missingRequiredOptions.add( required );
+        }
 
-            if ( optionsHasAnyOf( options, eachEntry.getValue() ) && !options.has( required ) ) {
-                missingRequiredOptions.addAll( required.options() );
+        return missingRequiredOptions;
+    }
+
+    private List<AbstractOptionSpec<?>> unavailableOptions(OptionSet options) {
+        List<AbstractOptionSpec<?>> unavailableOptions = new ArrayList<>();
+
+        for ( Map.Entry<List<String>, Set<OptionSpec<?>>> eachEntry : availableIf.entrySet() ) {
+            AbstractOptionSpec<?> forbidden = specFor( eachEntry.getKey().iterator().next() );
+
+            if ( !optionsHasAnyOf( options, eachEntry.getValue() ) && options.has( forbidden ) ) {
+                unavailableOptions.add(forbidden);
             }
         }
 
-        for ( Map.Entry<Collection<String>, Set<OptionSpec<?>>> eachEntry : requiredUnless.entrySet() ) {
-            AbstractOptionSpec<?> required = specFor( eachEntry.getKey().iterator().next() );
+        for ( Map.Entry<List<String>, Set<OptionSpec<?>>> eachEntry : availableUnless.entrySet() ) {
+            AbstractOptionSpec<?> forbidden = specFor( eachEntry.getKey().iterator().next() );
 
-            if ( !optionsHasAnyOf( options, eachEntry.getValue() ) && !options.has( required ) ) {
-                missingRequiredOptions.addAll( required.options() );
+            if ( optionsHasAnyOf( options, eachEntry.getValue() ) && options.has( forbidden ) ) {
+                unavailableOptions.add(forbidden);
             }
         }
 
-        return missingRequiredOptions;
+        return unavailableOptions;
     }
 
     private boolean optionsHasAnyOf( OptionSet options, Collection<OptionSpec<?>> specs ) {
@@ -443,12 +522,14 @@
 
     private boolean isHelpOptionPresent( OptionSet options ) {
         boolean helpOptionPresent = false;
+
         for ( AbstractOptionSpec<?> each : recognizedOptions.toJavaUtilMap().values() ) {
             if ( each.isForHelp() && options.has( each ) ) {
                 helpOptionPresent = true;
                 break;
             }
         }
+
         return helpOptionPresent;
     }
 
@@ -505,24 +586,40 @@
         return recognizedOptions.contains( option );
     }
 
-    void requiredIf( Collection<String> precedentSynonyms, String required ) {
+    void requiredIf( List<String> precedentSynonyms, String required ) {
         requiredIf( precedentSynonyms, specFor( required ) );
     }
 
-    void requiredIf( Collection<String> precedentSynonyms, OptionSpec<?> required ) {
-        putRequiredOption( precedentSynonyms, required, requiredIf );
+    void requiredIf( List<String> precedentSynonyms, OptionSpec<?> required ) {
+        putDependentOption( precedentSynonyms, required, requiredIf );
     }
 
-    void requiredUnless( Collection<String> precedentSynonyms, String required ) {
+    void requiredUnless( List<String> precedentSynonyms, String required ) {
         requiredUnless( precedentSynonyms, specFor( required ) );
     }
 
-    void requiredUnless( Collection<String> precedentSynonyms, OptionSpec<?> required ) {
-        putRequiredOption( precedentSynonyms, required, requiredUnless );
+    void requiredUnless( List<String> precedentSynonyms, OptionSpec<?> required ) {
+        putDependentOption( precedentSynonyms, required, requiredUnless );
+    }
+
+    void availableIf( List<String> precedentSynonyms, String available ) {
+        availableIf( precedentSynonyms, specFor( available ) );
     }
 
-    private void putRequiredOption( Collection<String> precedentSynonyms, OptionSpec<?> required,
-        Map<Collection<String>, Set<OptionSpec<?>>> target ) {
+    void availableIf( List<String> precedentSynonyms, OptionSpec<?> available) {
+        putDependentOption( precedentSynonyms, available, availableIf );
+    }
+
+    void availableUnless( List<String> precedentSynonyms, String available ) {
+        availableUnless( precedentSynonyms, specFor( available ) );
+    }
+
+    void availableUnless( List<String> precedentSynonyms, OptionSpec<?> available ) {
+        putDependentOption( precedentSynonyms, available, availableUnless );
+    }
+
+    private void putDependentOption( List<String> precedentSynonyms, OptionSpec<?> required,
+        Map<List<String>, Set<OptionSpec<?>>> target ) {
 
         for ( String each : precedentSynonyms ) {
             AbstractOptionSpec<?> spec = specFor( each );
@@ -532,7 +629,7 @@
 
         Set<OptionSpec<?>> associated = target.get( precedentSynonyms );
         if ( associated == null ) {
-            associated = new HashSet<OptionSpec<?>>();
+            associated = new HashSet<>();
             target.put( precedentSynonyms, associated );
         }