3535import de .tototec .cmdoption .handler .PutIntoMapHandler ;
3636import de .tototec .cmdoption .handler .StringFieldHandler ;
3737import de .tototec .cmdoption .handler .StringMethodHandler ;
38+ import de .tototec .cmdoption .internal .F0 ;
3839import de .tototec .cmdoption .internal .F1 ;
3940import de .tototec .cmdoption .internal .FList ;
4041import de .tototec .cmdoption .internal .I18n ;
@@ -114,6 +115,8 @@ public class CmdlineParser {
114115
115116 private Optional <String > argsFromFilePrefix = Optional .some ("@" );
116117
118+ private Optional <String > aggregateShortOptionsWithPrefix = Optional .none ();
119+
117120 protected CmdlineParser (final CmdlineParser parent , final String commandName , final Object commandObject ) {
118121 this .parent = parent ;
119122 debugAllowed = parent .debugAllowed ;
@@ -359,9 +362,35 @@ public List<String> apply(final String arg) {
359362
360363 boolean helpDetected = false ;
361364
362- // Actually iterate over the command line elements
363- for (int index = 0 ; index < cmdline .length ; ++index ) {
364- final String param = cmdline [index ];
365+ final String aggregatePrefix = aggregateShortOptionsWithPrefix .getOrElse (new F0 <String >() {
366+ @ Override
367+ public String apply () {
368+ return "" ;
369+ }
370+ });
371+ final LinkedHashMap <String , OptionHandle > shortOptionMap = new LinkedHashMap <String , OptionHandle >();
372+ final int aggregatePrefixSize = aggregatePrefix .length ();
373+ if (aggregateShortOptionsWithPrefix .isDefined ()) {
374+ final int expectedSize = 1 + aggregatePrefixSize ;
375+ for (final Entry <String , OptionHandle > oh : quickOptionMap .entrySet ()) {
376+ if (oh .getKey ().startsWith (aggregatePrefix ) && oh .getKey ().length () == expectedSize ) {
377+ shortOptionMap .put (oh .getKey ().substring (aggregatePrefixSize ), oh .getValue ());
378+ }
379+ }
380+ }
381+
382+ int index = -1 ;
383+ String [] rest = cmdline ;
384+
385+ while (rest .length > index + 1 ) {
386+ if (index >= 0 ) {
387+ rest = Arrays .copyOfRange (rest , ++index , rest .length );
388+ }
389+ index = 0 ;
390+
391+ // Actually iterate over the command line elements
392+ // for (int index = 0; index < cmdline.length; ++index) {
393+ final String param = rest [index ];
365394 if (parseOptions && stopOption .equals (param )) {
366395 parseOptions = false ;
367396
@@ -380,20 +409,20 @@ public List<String> apply(final String arg) {
380409 helpDetected = true ;
381410 }
382411
383- if (cmdline .length <= index + optionHandle .getArgsCount ()) {
412+ if (rest .length <= index + optionHandle .getArgsCount ()) {
384413 final PreparedI18n msg = i18n .preparetr (
385414 "Missing arguments(s): {0}. Option \" {1}\" requires {2} arguments, but you gave {3}." ,
386415 FList .mkString (
387- Arrays .asList (optionHandle .getArgs ()).subList (cmdline .length - index - 1 ,
416+ Arrays .asList (optionHandle .getArgs ()).subList (rest .length - index - 1 ,
388417 optionHandle .getArgsCount ()),
389418 ", " ),
390419 param , optionHandle
391420 .getArgsCount (),
392- cmdline .length - index - 1 );
421+ rest .length - index - 1 );
393422 throw new CmdlineParserException (msg .notr (), msg .tr ());
394423 }
395424 // slurp next cmdline arguments into option arguments
396- final String [] optionArgs = Arrays .copyOfRange (cmdline , index + 1 ,
425+ final String [] optionArgs = Arrays .copyOfRange (rest , index + 1 ,
397426 index + 1 + optionHandle .getArgsCount ());
398427 index += optionHandle .getArgsCount ();
399428
@@ -427,9 +456,40 @@ public List<String> apply(final String arg) {
427456 }
428457 // Delegate parsing of the rest of the cmdline to the command
429458 commandHandle .getCmdlineParser ().parse (dryrun , detectHelpAndSkipValidation ,
430- Arrays .copyOfRange (cmdline , index + 1 , cmdline .length ));
459+ Arrays .copyOfRange (rest , index + 1 , rest .length ));
431460 // Stop parsing
432461 break ;
462+ } else if (parseOptions
463+ && aggregateShortOptionsWithPrefix .isDefined ()
464+ && param .startsWith (aggregatePrefix )
465+ && param .length () > aggregatePrefixSize + 1 ) {
466+ // Found an aggregated short option
467+ final char [] singleOptions = param .substring (aggregatePrefixSize ).toCharArray ();
468+ // rewrite the cmdline
469+ final List <String > rewritten = new LinkedList <String >();
470+ int procCount = 1 ;
471+ for (final char c : singleOptions ) {
472+ final OptionHandle oh = shortOptionMap .get (String .valueOf (c ));
473+ if (oh == null ) {
474+ // FIXME: unsupported aggregation found
475+ }
476+ if (rest .length < procCount + oh .getArgsCount ()) {
477+ // FIXME: missing args detected
478+ }
479+ // add as standalone short option
480+ rewritten .add (aggregatePrefix + c );
481+ for (int i = 0 ; i < oh .getArgsCount (); ++i ) {
482+ // slurp args from cmdline
483+ rewritten .add (rest [i + procCount ]);
484+ ++procCount ;
485+ }
486+ }
487+ // re-interate parsing with the modified command line
488+ final String [] newRest = Arrays .copyOfRange (rest , procCount , rest .length );
489+ rewritten .addAll (Arrays .asList (newRest ));
490+ rest = rewritten .toArray (new String [0 ]);
491+ index = -1 ;
492+ continue ;
433493
434494 } else if (parameter == null && defaultCommandName != null
435495 && quickCommandMap .containsKey (defaultCommandName )) {
@@ -442,24 +502,24 @@ public List<String> apply(final String arg) {
442502 }
443503 // Delegate parsing of the rest of the cmdline to the command
444504 commandHandle .getCmdlineParser ().parse (dryrun , detectHelpAndSkipValidation ,
445- Arrays .copyOfRange (cmdline , index , cmdline .length ));
505+ Arrays .copyOfRange (rest , index , rest .length ));
446506 // Stop parsing
447507 break ;
448508
449509 } else if (parameter != null ) {
450510 // Found a parameter
451511 optionCount .put (parameter , optionCount .get (parameter ) + 1 );
452512
453- if (cmdline .length <= index + parameter .getArgsCount () - 1 ) {
454- final int countOfGivenParams = cmdline .length - index ;
513+ if (rest .length <= index + parameter .getArgsCount () - 1 ) {
514+ final int countOfGivenParams = rest .length - index ;
455515 final PreparedI18n msg = i18n .preparetr (
456516 "Missing arguments: {0} Parameter requires {1} arguments, but you gave {2}." ,
457517 Arrays .asList (parameter .getArgs ()).subList (countOfGivenParams , parameter .getArgsCount ()),
458518 parameter .getArgsCount (), countOfGivenParams );
459519 throw new CmdlineParserException (msg .notr (), msg .tr ());
460520 }
461521 // slurp next cmdline arguments into option arguments
462- final String [] optionArgs = Arrays .copyOfRange (cmdline , index , index + parameter .getArgsCount ());
522+ final String [] optionArgs = Arrays .copyOfRange (rest , index , index + parameter .getArgsCount ());
463523 // -1, because index gets increased by one at end of for-loop
464524 index += parameter .getArgsCount () - 1 ;
465525
@@ -704,6 +764,8 @@ protected void validateOptions() {
704764 }
705765 }
706766 }
767+ // TODO: Ensure, there are no long options, starting with the aggregated short
768+ // option prefix, and when, disable this feature
707769 }
708770
709771 protected boolean isVisible (final Class <?> baseClass , final Member element ) {
@@ -1025,4 +1087,12 @@ public void setReadArgsFromFilePrefix(final String prefix) {
10251087 }
10261088 }
10271089
1090+ public void setAggregateShortOptionsWithPrefix (final String prefix ) {
1091+ if (prefix == null || prefix .trim ().isEmpty ()) {
1092+ aggregateShortOptionsWithPrefix = Optional .none ();
1093+ } else {
1094+ aggregateShortOptionsWithPrefix = Optional .some (prefix .trim ());
1095+ }
1096+ }
1097+
10281098}
0 commit comments