@@ -149,6 +149,8 @@ class DefaultNodeVisitor extends AbstractNodeVisitor {
149
149
private List <OptionNode > invalidOptionNodes = new ArrayList <>();
150
150
private List <ArgumentResult > argumentResults = new ArrayList <>();
151
151
private int commandArgumentPos = 0 ;
152
+ private int optionPos = -1 ;
153
+ private long expectedOptionCount ;
152
154
153
155
DefaultNodeVisitor (CommandModel commandModel , ConversionService conversionService , ParserConfig config ) {
154
156
this .commandModel = commandModel ;
@@ -166,31 +168,27 @@ protected ParseResult buildResult() {
166
168
messageResults .addAll (commonMessageResults );
167
169
messageResults .addAll (validateOptionIsValid (registration ));
168
170
169
-
170
- // add options with default values
171
- Set <CommandOption > resolvedOptions1 = optionResults .stream ()
171
+ // we should already have options defined with arguments.
172
+ // go through positional arguments and fill using those and
173
+ // then fill from option default values.
174
+ Set <CommandOption > resolvedOptions = optionResults .stream ()
172
175
.map (or -> or .option ())
173
176
.collect (Collectors .toSet ());
174
- registration .getOptions ().stream ()
175
- .filter (o -> o .getDefaultValue () != null )
176
- .filter (o -> !resolvedOptions1 .contains (o ))
177
- .forEach (o -> {
178
- resolvedOptions1 .add (o );
179
- Object value = convertOptionType (o , o .getDefaultValue ());
180
- optionResults .add (OptionResult .of (o , value ));
181
- });
182
177
178
+ // get sorted list by position as we later match by order
183
179
List <CommandOption > optionsForArguments = registration .getOptions ().stream ()
184
- .filter (o -> !resolvedOptions1 .contains (o ))
180
+ .filter (o -> !resolvedOptions .contains (o ))
185
181
.filter (o -> o .getPosition () > -1 )
186
182
.sorted (Comparator .comparingInt (o -> o .getPosition ()))
187
183
.collect (Collectors .toList ());
188
184
185
+ // leftover arguments to match into needed options
189
186
List <String > argumentValues = argumentResults .stream ()
190
187
.sorted (Comparator .comparingInt (ar -> ar .position ()))
191
188
.map (ar -> ar .value ())
192
189
.collect (Collectors .toList ());
193
190
191
+ // try to find matching arguments
194
192
int i = 0 ;
195
193
for (CommandOption o : optionsForArguments ) {
196
194
int aMax = o .getArityMax ();
@@ -202,11 +200,17 @@ protected ParseResult buildResult() {
202
200
203
201
List <String > asdf = argumentValues .subList (i , j );
204
202
if (asdf .isEmpty ()) {
205
- optionResults .add (OptionResult .of (o , null ));
203
+ // don't arguments so only add if we know
204
+ // it's going to get added later via default value
205
+ if (o .getDefaultValue () == null ) {
206
+ resolvedOptions .add (o );
207
+ optionResults .add (OptionResult .of (o , null ));
208
+ }
206
209
}
207
210
else {
208
211
Object toConvertValue = asdf .size () == 1 ? asdf .get (0 ) : asdf ;
209
212
Object value = convertOptionType (o , toConvertValue );
213
+ resolvedOptions .add (o );
210
214
optionResults .add (OptionResult .of (o , value ));
211
215
}
212
216
@@ -216,6 +220,16 @@ protected ParseResult buildResult() {
216
220
i = j ;
217
221
}
218
222
223
+ // possibly fill in from default values
224
+ registration .getOptions ().stream ()
225
+ .filter (o -> o .getDefaultValue () != null )
226
+ .filter (o -> !resolvedOptions .contains (o ))
227
+ .forEach (o -> {
228
+ resolvedOptions .add (o );
229
+ Object value = convertOptionType (o , o .getDefaultValue ());
230
+ optionResults .add (OptionResult .of (o , value ));
231
+ });
232
+
219
233
// can only validate after optionResults has been populated
220
234
messageResults .addAll (validateOptionNotMissing (registration ));
221
235
}
@@ -234,6 +248,7 @@ protected void onExitDirectiveNode(DirectiveNode node) {
234
248
235
249
@ Override
236
250
protected void onEnterRootCommandNode (CommandNode node ) {
251
+ expectedOptionCount = optionCountInCommand (node );
237
252
resolvedCommmand .add (node .getCommand ());
238
253
}
239
254
@@ -243,6 +258,7 @@ protected void onExitRootCommandNode(CommandNode node) {
243
258
244
259
@ Override
245
260
protected void onEnterCommandNode (CommandNode node ) {
261
+ expectedOptionCount = optionCountInCommand (node );
246
262
resolvedCommmand .add (node .getCommand ());
247
263
}
248
264
@@ -254,6 +270,7 @@ protected void onExitCommandNode(CommandNode node) {
254
270
255
271
@ Override
256
272
protected void onEnterOptionNode (OptionNode node ) {
273
+ optionPos ++;
257
274
commandArgumentPos = 0 ;
258
275
currentOptions .clear ();
259
276
currentOptionArgument .clear ();
@@ -307,16 +324,40 @@ protected void onExitOptionNode(OptionNode node) {
307
324
int max = currentOption .getArityMax () > 0 ? currentOption .getArityMax () : Integer .MAX_VALUE ;
308
325
max = Math .min (max , currentOptionArgument .size ());
309
326
List <String > toUse = currentOptionArgument .subList (0 , max );
327
+ List <String > toUnused = currentOptionArgument .subList (max , currentOptionArgument .size ());
328
+ toUnused .forEach (a -> {
329
+ argumentResults .add (ArgumentResult .of (a , commandArgumentPos ++));
330
+ });
310
331
311
- if (currentOption .getArityMin () > -1 && currentOptionArgument .size () < currentOption .getArityMin ()) {
312
- String arg = currentOption .getLongNames ()[0 ];
313
- commonMessageResults .add (MessageResult .of (ParserMessage .NOT_ENOUGH_OPTION_ARGUMENTS , 0 , arg ,
314
- currentOptionArgument .size ()));
332
+ // if we're not in a last option
333
+ // "--arg1 a b --arg2 c" vs "--arg1 a --arg2 b c"
334
+ // we know to impose restriction to argument count,
335
+ // last option is different as we can't really fail
336
+ // because number of argument to eat dependes on arity
337
+ // and rest would go back to positional args.
338
+ if (optionPos + 1 < expectedOptionCount ) {
339
+ if (currentOption .getArityMin () > -1 && currentOptionArgument .size () < currentOption .getArityMin ()) {
340
+ String arg = currentOption .getLongNames ()[0 ];
341
+ commonMessageResults .add (MessageResult .of (ParserMessage .NOT_ENOUGH_OPTION_ARGUMENTS , 0 , arg ,
342
+ currentOptionArgument .size ()));
343
+ }
344
+ else if (currentOption .getArityMax () > -1 && currentOptionArgument .size () > currentOption .getArityMax ()) {
345
+ String arg = currentOption .getLongNames ()[0 ];
346
+ commonMessageResults .add (MessageResult .of (ParserMessage .TOO_MANY_OPTION_ARGUMENTS , 0 , arg ,
347
+ currentOption .getArityMax ()));
348
+ }
315
349
}
316
- else if (currentOption .getArityMax () > -1 && currentOptionArgument .size () > currentOption .getArityMax ()) {
317
- String arg = currentOption .getLongNames ()[0 ];
318
- commonMessageResults .add (MessageResult .of (ParserMessage .TOO_MANY_OPTION_ARGUMENTS , 0 , arg ,
319
- currentOption .getArityMax ()));
350
+ else {
351
+ if (currentOption .getArityMin () > -1 && toUse .size () < currentOption .getArityMin ()) {
352
+ String arg = currentOption .getLongNames ()[0 ];
353
+ commonMessageResults .add (MessageResult .of (ParserMessage .NOT_ENOUGH_OPTION_ARGUMENTS , 0 , arg ,
354
+ toUse .size ()));
355
+ }
356
+ else if (currentOption .getArityMax () > -1 && toUse .size () > currentOption .getArityMax ()) {
357
+ String arg = currentOption .getLongNames ()[0 ];
358
+ commonMessageResults .add (MessageResult .of (ParserMessage .TOO_MANY_OPTION_ARGUMENTS , 0 , arg ,
359
+ currentOption .getArityMax ()));
360
+ }
320
361
}
321
362
322
363
Object value = null ;
@@ -415,5 +456,12 @@ private List<MessageResult> validateOptionIsValid(CommandRegistration registrati
415
456
})
416
457
.collect (Collectors .toList ());
417
458
}
459
+
460
+ private static long optionCountInCommand (CommandNode node ) {
461
+ if (node == null ) {
462
+ return 0 ;
463
+ }
464
+ return node .getChildren ().stream ().filter (n -> n instanceof OptionNode ).count ();
465
+ }
418
466
}
419
467
}
0 commit comments