@@ -83,22 +83,22 @@ public static ImmutableList<OptionDefinition> getAllOptionDefinitionsForClass(
83
83
84
84
/**
85
85
* Mapping from each options class to its no-arg constructor. Entries appear in the same order
86
- * that they were passed to {@link #from(Collection)}.
86
+ * that they were passed to {@link #from(Collection, boolean )}.
87
87
*/
88
88
private final ImmutableMap <Class <? extends OptionsBase >, Constructor <?>> optionsClasses ;
89
89
90
90
/**
91
91
* Mapping from option name to {@code OptionDefinition}. Entries appear ordered first by their
92
- * options class (the order in which they were passed to {@link #from(Collection)}, and then in
93
- * alphabetic order within each options class.
92
+ * options class (the order in which they were passed to {@link #from(Collection, boolean )}, and
93
+ * then in alphabetic order within each options class.
94
94
*/
95
95
private final ImmutableMap <String , OptionDefinition > nameToField ;
96
96
97
97
/**
98
- * For options that have an "OldName", this is a mapping from old name to its corresponding {@code
99
- * OptionDefinition}. Entries appear ordered first by their options class (the order in which they
100
- * were passed to {@link #from(Collection)}, and then in alphabetic order within each options
101
- * class.
98
+ * For options that have an "OldName", this is a mapping from old name to its corresponding
99
+ * {@code OptionDefinition}. Entries appear ordered first by their options class (the order in
100
+ * which they were passed to {@link #from(Collection, boolean )}, and then in alphabetic order
101
+ * within each options class.
102
102
*/
103
103
private final ImmutableMap <String , OptionDefinition > oldNameToField ;
104
104
@@ -136,7 +136,7 @@ protected IsolatedOptionsData(IsolatedOptionsData other) {
136
136
137
137
/**
138
138
* Returns all options classes indexed by this options data object, in the order they were passed
139
- * to {@link #from(Collection)}.
139
+ * to {@link #from(Collection, boolean )}.
140
140
*/
141
141
public Collection <Class <? extends OptionsBase >> getOptionsClasses () {
142
142
return optionsClasses .keySet ();
@@ -157,8 +157,8 @@ public OptionDefinition getOptionDefinitionFromName(String name) {
157
157
158
158
/**
159
159
* Returns all {@link OptionDefinition} objects loaded, mapped by their canonical names. Entries
160
- * appear ordered first by their options class (the order in which they were passed to {@link
161
- * #from(Collection)}, and then in alphabetic order within each options class.
160
+ * appear ordered first by their options class (the order in which they were passed to
161
+ * {@link #from(Collection, boolean )}, and then in alphabetic order within each options class.
162
162
*/
163
163
public Iterable <Map .Entry <String , OptionDefinition >> getAllOptionDefinitions () {
164
164
return nameToField .entrySet ();
@@ -177,9 +177,15 @@ public boolean getUsesOnlyCoreTypes(Class<? extends OptionsBase> optionsClass) {
177
177
* both single-character abbreviations and full names.
178
178
*/
179
179
private static <A > void checkForCollisions (
180
- Map <A , OptionDefinition > aFieldMap , A optionName , String description )
180
+ Map <A , OptionDefinition > aFieldMap , A optionName , OptionDefinition definition ,
181
+ String description , boolean allowDuplicatesParsingEquivalently )
181
182
throws DuplicateOptionDeclarationException {
182
183
if (aFieldMap .containsKey (optionName )) {
184
+ OptionDefinition otherDefinition = aFieldMap .get (optionName );
185
+ if (allowDuplicatesParsingEquivalently && OptionsParserImpl .equivalentForParsing (
186
+ otherDefinition , definition )) {
187
+ return ;
188
+ }
183
189
throw new DuplicateOptionDeclarationException (
184
190
"Duplicate option name, due to " + description + ": --" + optionName );
185
191
}
@@ -212,22 +218,30 @@ private static void checkAndUpdateBooleanAliases(
212
218
Map <String , OptionDefinition > nameToFieldMap ,
213
219
Map <String , OptionDefinition > oldNameToFieldMap ,
214
220
Map <String , String > booleanAliasMap ,
215
- String optionName )
221
+ String optionName ,
222
+ OptionDefinition optionDefinition ,
223
+ boolean allowDuplicatesParsingEquivalently )
216
224
throws DuplicateOptionDeclarationException {
217
225
// Check that the negating alias does not conflict with existing flags.
218
- checkForCollisions (nameToFieldMap , "no" + optionName , "boolean option alias" );
219
- checkForCollisions (oldNameToFieldMap , "no" + optionName , "boolean option alias" );
226
+ checkForCollisions (nameToFieldMap , "no" + optionName , optionDefinition , "boolean option alias" ,
227
+ allowDuplicatesParsingEquivalently );
228
+ checkForCollisions (oldNameToFieldMap , "no" + optionName , optionDefinition ,
229
+ "boolean option alias" , allowDuplicatesParsingEquivalently );
220
230
221
231
// Record that the boolean option takes up additional namespace for its negating alias.
222
232
booleanAliasMap .put ("no" + optionName , optionName );
223
233
}
224
234
225
235
/**
226
- * Constructs an {@link IsolatedOptionsData} object for a parser that knows about the given {@link
227
- * OptionsBase} classes. No inter-option analysis is done. Performs basic validity checks on each
228
- * option in isolation.
236
+ * Constructs an {@link IsolatedOptionsData} object for a parser that knows about the given
237
+ * {@link OptionsBase} classes. No inter-option analysis is done. Performs basic validity checks
238
+ * on each option in isolation.
239
+ *
240
+ * <p>If {@code allowDuplicatesParsingEquivalently} is true, then options that collide in name but
241
+ * parse equivalently (e.g. both of them accept a value or both of them do not), are allowed.
229
242
*/
230
- static IsolatedOptionsData from (Collection <Class <? extends OptionsBase >> classes ) {
243
+ static IsolatedOptionsData from (Collection <Class <? extends OptionsBase >> classes ,
244
+ boolean allowDuplicatesParsingEquivalently ) {
231
245
// Mind which fields have to preserve order.
232
246
Map <Class <? extends OptionsBase >, Constructor <?>> constructorBuilder = new LinkedHashMap <>();
233
247
Map <String , OptionDefinition > nameToFieldBuilder = new LinkedHashMap <>();
@@ -256,15 +270,18 @@ static IsolatedOptionsData from(Collection<Class<? extends OptionsBase>> classes
256
270
for (OptionDefinition optionDefinition : optionDefinitions ) {
257
271
try {
258
272
String optionName = optionDefinition .getOptionName ();
259
- checkForCollisions (nameToFieldBuilder , optionName , "option name collision" );
273
+ checkForCollisions (nameToFieldBuilder , optionName , optionDefinition ,
274
+ "option name collision" , allowDuplicatesParsingEquivalently );
260
275
checkForCollisions (
261
276
oldNameToFieldBuilder ,
262
277
optionName ,
263
- "option name collision with another option's old name" );
278
+ optionDefinition ,
279
+ "option name collision with another option's old name" ,
280
+ allowDuplicatesParsingEquivalently );
264
281
checkForBooleanAliasCollisions (booleanAliasMap , optionName , "option" );
265
282
if (optionDefinition .usesBooleanValueSyntax ()) {
266
- checkAndUpdateBooleanAliases (
267
- nameToFieldBuilder , oldNameToFieldBuilder , booleanAliasMap , optionName );
283
+ checkAndUpdateBooleanAliases (nameToFieldBuilder , oldNameToFieldBuilder , booleanAliasMap ,
284
+ optionName , optionDefinition , allowDuplicatesParsingEquivalently );
268
285
}
269
286
nameToFieldBuilder .put (optionName , optionDefinition );
270
287
@@ -273,23 +290,27 @@ static IsolatedOptionsData from(Collection<Class<? extends OptionsBase>> classes
273
290
checkForCollisions (
274
291
nameToFieldBuilder ,
275
292
oldName ,
276
- "old option name collision with another option's canonical name" );
293
+ optionDefinition ,
294
+ "old option name collision with another option's canonical name" ,
295
+ allowDuplicatesParsingEquivalently );
277
296
checkForCollisions (
278
297
oldNameToFieldBuilder ,
279
298
oldName ,
280
- "old option name collision with another old option name" );
299
+ optionDefinition ,
300
+ "old option name collision with another old option name" ,
301
+ allowDuplicatesParsingEquivalently );
281
302
checkForBooleanAliasCollisions (booleanAliasMap , oldName , "old option name" );
282
303
// If boolean, repeat the alias dance for the old name.
283
304
if (optionDefinition .usesBooleanValueSyntax ()) {
284
- checkAndUpdateBooleanAliases (
285
- nameToFieldBuilder , oldNameToFieldBuilder , booleanAliasMap , oldName );
305
+ checkAndUpdateBooleanAliases (nameToFieldBuilder , oldNameToFieldBuilder ,
306
+ booleanAliasMap , oldName , optionDefinition , allowDuplicatesParsingEquivalently );
286
307
}
287
308
// Now that we've checked for conflicts, confidently store the old name.
288
309
oldNameToFieldBuilder .put (oldName , optionDefinition );
289
310
}
290
311
if (optionDefinition .getAbbreviation () != '\0' ) {
291
- checkForCollisions (
292
- abbrevToFieldBuilder , optionDefinition . getAbbreviation () , "option abbreviation" );
312
+ checkForCollisions (abbrevToFieldBuilder , optionDefinition . getAbbreviation (),
313
+ optionDefinition , "option abbreviation" , allowDuplicatesParsingEquivalently );
293
314
abbrevToFieldBuilder .put (optionDefinition .getAbbreviation (), optionDefinition );
294
315
}
295
316
} catch (DuplicateOptionDeclarationException e ) {
0 commit comments