001package Torello.Java.Build;
002
003import Apache.CLI.*;
004
005import static Torello.Java.C.*;
006
007import Torello.Java.UnreachableError;
008import Torello.Java.StrCmpr;
009
010import Torello.Java.ReadOnly.ReadOnlyList;
011import Torello.Java.ReadOnly.ReadOnlyArrayList;
012
013import java.util.ArrayList;
014import java.util.List;
015import java.util.stream.Stream;
016import java.io.File;
017
018/**
019 * This data class is generated by parsing the Command-Line Input to a Builder Invocation, and it
020 * contains the settings that were extrapolated by the Apache CLI Parser.  When a {@link Builder}
021 * instance is created, its constructor requires that a Java {@code `String[] argv`} array be
022 * included as a Constructor-Parameter.  The provided {@code String[]}-Array is used to build an
023 * instance of this class.
024 * 
025 * <BR /><BR />The data contained as fields of this class is all declared {@code public} and also
026 * {@code ReadOnly} or {@code final}.  The instance generated from the Command-Line Input (CLI)
027 * parser is available as a {@code public} and {@code final} field of the {@link Builder} instance
028 * field {@link Builder#cli Torello.Java.Build.Builder.cli};
029 * 
030 * <BR /><BR />This class does not offer any public constructors, nor any public methods.  It is 
031 * automatically built by class {@code Builder}, which has package-level access to this class' sole
032 * constructor.
033 * 
034 * <EMBED CLASS='external-html' DATA-FILE-ID=CLI_SWITCHES>
035 */
036public class CLI
037{
038    // ********************************************************************************************
039    // ********************************************************************************************
040    // Fields
041    // ********************************************************************************************
042    // ********************************************************************************************
043
044
045    public final String     MENU_CHOICE;
046    public final boolean    INCLUDE_JAR_IN_CP;
047    public final boolean    SKIP_REMOVE_GCS_FILES;
048    public final boolean    JAR_ONLY;
049    public final boolean    INCLUDE_EARLY_DEV_PACKAGES;
050    public final boolean    toReleaseOrDeveloper;
051    public final boolean    QUICKER_BUILD;
052    public final boolean    OVERRIDE_TOGGLE_USE_XLINT_SWITCH;
053    public final boolean    OVERRIDE_TOGGLE_USE_XDIAGS_SWITCH;
054    public final String     GCS_DIR;
055
056    // Option #1 and #21.  Note, it was decided both of these shall be 'null' instead of
057    // empty-lists (when they would otherwise be empty-lists)
058
059    public final ReadOnlyList<BuildPackage>    userSpecifiedPackages;
060    public final ReadOnlyList<String>          userProvidedNickNames;
061
062
063    // ********************************************************************************************
064    // ********************************************************************************************
065    // Main-Menu Options-Group
066    // ********************************************************************************************
067    // ********************************************************************************************
068
069
070    // This generates an "OptionGroup" that contains all of the choices for the Main-Menu
071    // Exactly one of these options must be chosen by the user.
072    //
073    // If zero **OR** more than one is chosen, then the CLI-Constructor will throw an exception and
074    // print an error message
075    //
076    // The Options Include:
077    //  * 8 Individual Build-Steps
078    //  * 3 Complete-Build Composite-Steps (These perform steps 1 through 7/8)
079    //  * 2 Developer-Build Composite-Steps (These perform a subset of steps 1 - 8)
080
081    private static OptionGroup mainOptions()
082    {
083        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
084        // Primary Build-Step Options
085        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
086
087        final Option javac = Option
088            .builder        ("1")
089            .required       (false)
090            .desc           ("Build Stage 1: Java-Compiler")
091            .longOpt        ("javac")
092            .hasArgs        ()
093            .optionalArg    (true)
094            .valueSeparator (' ')
095            .argName        ("PackageNickNames")
096            .build();
097
098        final Option javaDoc = Option
099            .builder    ("2")
100            .required   (false)
101            .desc       ("Build Stage 2: Standard 'javadoc' Tool")
102            .longOpt    ("javadoc")
103            .hasArg     (false)
104            .build();
105
106        final Option jdu = Option
107            .builder    ("3")
108            .required   (false)
109            .desc       ("Build Stage 3: Torello Java-Doc Upgrader")
110            .longOpt    ("jdu")
111            .hasArg     (false)
112            .build();
113
114        final Option tarJar = Option
115            .builder        ("4")
116            .required       (false)
117            .desc           ("Build Stage 4, Archive-Files")
118            .longOpt        ("tarjar")
119            .optionalArg    (true)  // "JARONLY"
120            .valueSeparator (' ')
121            .argName        ("JARONLY")
122            .build();
123
124
125        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
126        // Sync-Step Options
127        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
128
129        final Option syncJavaDoc = Option
130            .builder    ("5")
131            .required   (false)
132            .desc       ("Build Stage 5: Sync 'javadoc/'")
133            .longOpt    ("syncJavaDoc")
134            .hasArg     (false)
135            .build();
136
137        final Option syncTarJar = Option
138            .builder    ("6")
139            .required   (false)
140            .desc       ("Build Stage 6: Sync Tar & Jar")
141            .longOpt    ("syncTarJar")
142            .hasArg     (false)
143            .build();
144
145        final Option syncLogs = Option
146            .builder    ("7")
147            .required   (false)
148            .desc       ("Build Stage 7: Sync Log-Files")
149            .longOpt    ("syncLogs")
150            .hasArg     (false)
151            .build();
152
153        final Option setMaxAge = Option
154            .builder    ("8")
155            .required   (false)
156            .desc       ("Build Stage 8: Set Max-Age Browser-Cache")
157            .longOpt    ("setMaxAge")
158            .hasArg     (false)
159            .build();
160
161
162        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
163        // Composite-Step / Complete-Build Options
164        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
165
166        final Option buildRel = Option
167            .builder    ("cb1")
168            .required   (false)
169            .desc       ("Complete Build: STEPS 2 to 7. Sync-To: GCP-Release")
170            .longOpt    ("buildRel")
171            .hasArg     (false)
172            .build();
173
174        final Option buildDevSMA = Option
175            .builder    ("cb2")
176            .required   (false)
177            .desc       ("Complete Build: STEPS 2 to 8. Sync-To: GCP-Developer")
178            .longOpt    ("buildDevSMA")
179            .hasArg     (false)
180            .build();
181
182        final Option buildDev = Option
183            .builder    ("cb3")
184            .required   (false)
185            .desc       ("Complete Build: STEPS 2 to 7. Sync-To: GCP-Developer")
186            .longOpt    ("buildDev")
187            .hasArg     (false)
188            .build();
189
190
191        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
192        // Partial Debugging / Developer Build
193        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
194
195        final Option partialAll = Option
196            .builder    ("pb1")
197            .required   (false)
198            .desc       ("Partial-Debuging Build: STEPS 2 & 3. (Doc Only)")
199            .longOpt    ("partialAll")
200            .hasArg     (false)
201            .build();
202
203        final Option partialSome = Option
204            .builder        ("pb2")
205            .required       (false)
206            .desc           ("Partial-Debuging Build: STEPS 2, 3, 5, and 8. To: GCP-Developer")
207            .longOpt        ("partialSome")
208            .hasArgs        ()
209            .optionalArg    (true)
210            .valueSeparator (' ')
211            .argName        ("PackageNickNames")
212            .build();
213
214
215        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
216        // Run Links Checker
217        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
218
219        final Option linksCheck = Option
220            .builder    ("LC")
221            .required   (false)
222            .desc       ("Links Checker Build")
223            .longOpt    ("linksCheck")
224            .hasArg     (false)
225            .build();
226
227
228        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
229        // INSERT ALL INTO THE "OptionGroup" ... And return that instance
230        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
231
232        final OptionGroup group = new OptionGroup();
233
234        group.setRequired(true);
235        group
236            // Primary Build-Step Options
237            .addOption(javac)
238            .addOption(javaDoc)
239            .addOption(jdu)
240            .addOption(tarJar)
241
242            // Sync-Step Options
243            .addOption(syncJavaDoc)
244            .addOption(syncTarJar)
245            .addOption(syncLogs)
246            .addOption(setMaxAge)
247
248            // Composite-Step / Complete-Build Options
249            .addOption(buildRel)
250            .addOption(buildDevSMA)
251            .addOption(buildDev)
252
253            // Partial Debugging / Developer Build
254            .addOption(partialAll)
255            .addOption(partialSome)
256
257            // Run Links Checker
258            .addOption(linksCheck);
259
260        return group;
261    }
262
263
264    // ********************************************************************************************
265    // ********************************************************************************************
266    // Other / Extra Menu Options
267    // ********************************************************************************************
268    // ********************************************************************************************
269
270
271    // There are also three little "Extra Switches" that the user can provide / select.
272    // These three are just for convenience - making my life easier.
273
274    private static final String descriptionNQB =    
275        "Prevent a Quick Build during Partial-Build's";
276
277    private static final String descriptionJCP =
278        "Include the '.jar' File in the Class-Path";
279
280    private static final String descriptionSRG =
281        "Skip the removal-step of all Java-Doc GCS Files, when synchronizing.";
282
283    private static final String descriptionTXL =
284        "Toggle whatever the default setting is for applying -Xlint:all";
285
286    private static final String descriptionTXD =
287        "Toggle whatever the default setting is for applying -Xdiags:verbose";
288
289    private static final String descriptionIEDP =
290        "Include Packages still Under Early Development in the Build";
291
292
293    private static List<Option> otherNonGroupOptionNiceties()
294    {
295        final Option noQuickBuild = Option
296            .builder    ("NQB")
297            .required   (false)
298            .desc       (descriptionNQB)
299            .longOpt    ("noQuickBuild")
300            .hasArg     (false)
301            .build();
302
303        final Option putJarInCP = Option
304            .builder    ("JCP")
305            .required   (false)
306            .desc       (descriptionJCP)
307            .longOpt    ("putJarInCP")
308            .hasArg     (false)
309            .build();
310
311        final Option skipRemoveGCSFiles = Option
312            .builder    ("SRG")
313            .required   (false)
314            .desc       (descriptionSRG)
315            .longOpt    ("skipRemoveGCSFiles")
316            .hasArg     (false)
317            .build();
318
319        final Option overrideToggleXlintSwitch = Option
320            .builder    ("TXL")
321            .required   (false)
322            .desc       (descriptionTXL)
323            .longOpt    ("toggleDefaultXlint")
324            .hasArg     (false)
325            .build();
326
327        final Option overrideToggleXdiagsSwitch = Option
328            .builder    ("TXD")
329            .required   (false)
330            .desc       (descriptionTXD)
331            .longOpt    ("toggleDefaultXdiags")
332            .hasArg     (false)
333            .build();
334
335        final Option includeEarlyDevPkgsSwitch = Option
336            .builder    ("IEDP")
337            .required   (false)
338            .desc       (descriptionIEDP)
339            .longOpt    ("includeEarlyDev")
340            .hasArg     (false)
341            .build();
342
343        return List.of(
344            noQuickBuild,
345            putJarInCP,
346            skipRemoveGCSFiles,
347            overrideToggleXlintSwitch,
348            overrideToggleXdiagsSwitch,
349            includeEarlyDevPkgsSwitch
350        );
351    }
352
353
354    // ********************************************************************************************
355    // ********************************************************************************************
356    // Process the Command-Line Arguments
357    // ********************************************************************************************
358    // ********************************************************************************************
359
360
361    CLI(
362            String          GCS_DIR_DEV,
363            String          GCS_DIR_RELEASE,
364            BuildPackage[]  packageList,
365            String[]        argv
366        )
367    {
368        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
369        // Fill up Apache.CLI.Options (Get "Option" instances from the above two static methods)
370        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
371
372        final Options options = new Options();
373
374        // The "OptionGroup" instance contains the complete list of Menu-Options offered by this
375        // Build Package.  Exactly **ONE** of these Options should be selected by the user.
376
377        final OptionGroup optGroup = mainOptions();
378
379        // Add the main / top-level Command-Line Arguments
380        options.addOptionGroup(optGroup);
381
382        // Add the little extras.
383        for (Option o : otherNonGroupOptionNiceties()) options.addOption(o);
384
385
386        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
387        // Parse 'argv' using 'options' - Do some error checking
388        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
389
390        final CommandLine commandLine;
391
392        try
393        {
394            commandLine = DefaultParser
395                .builder()
396                .setAllowPartialMatching(false)
397                .setStripLeadingAndTrailingQuotes(true)
398                .build()
399                .parse(options, argv, false /* stopAtNonOption */);
400        }
401
402        // This happens when the user hasn't provided at least one of the "OptionGroup" switches
403        // All of the "Option" instances inside the "OptionGroup" are the **COMPLETE** list of 
404        // Menu-Options offered by this build.  Exactly 1 must be provided at the command line, or
405        // else this exception throws.  If so, just print an error message and exit.
406
407        catch (MissingOptionException moe)
408            { throw printMenuHelp(options, moe); }
409
410        // I haven't been through all of Apache's list of other possible error messages.  If there
411        // is one, print the Help-Menu, and the exception's message and exit.
412
413        catch (ParseException pe)
414            { throw printMenuHelp(options, pe); }
415
416
417        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
418        // Retrieve the Selected-Option's name, instance-reference, & number-as-byte
419        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
420
421        final String selectedOptName = optGroup.getSelected();
422
423        Option tempOpt = null;
424        for (Option o : optGroup.getOptions()) if (o.getOpt().equals(selectedOptName))
425        {
426            tempOpt = o;
427            break;
428        }
429
430        // It just looks better / smarter when variables that aren't going to change are actuallly
431        // declared final
432
433        final Option selectedOpt = tempOpt;
434
435        // The argv-option parsing mechanism should already have thrown an exception if the user
436        // failed to choose any of the provided options.
437
438        if (selectedOpt == null) throw new UnreachableError();
439
440        this.MENU_CHOICE = selectedOptName;
441
442
443        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
444        // More Error-Checking - This part can only be accomplished after filling in MENU_CHOICE
445        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
446        //
447        // There are only 2 menu-options that accept a list of package nick-names:
448        //
449        // "javac"  (Option #1) Allows you to compile only the packages listed, if these names
450        //          are included.
451        //
452        // "pb2"    (Partion-Build #2) Also allows the option of only Syncing-To-GCS the packages
453        //          listed
454
455        final boolean pkgNickNamesPossible =
456            this.MENU_CHOICE.equals("1") || this.MENU_CHOICE.equals("pb2");
457
458        // I simply cannot get Apache.CLI to allow for an Option that accepts ZERO, ONE or MANY
459        // arguments.  The possibilities with Apache.CLI seem to be either "ZERO or ONE" (an
460        // optional argument) **OR** "ONE-or-MORE" (multiple, but-not-zero).  Therefore, I sort of
461        // have to rely on this "hack-y" thing where I check the "extra non-recognized" switches
462        // to allow for the case of ZERO, ONE or MANY arguments to Options "-1" and "-pb2"
463
464        final String[] extraneousArgs = commandLine.getArgs();
465
466        // I can never tell (anywhere, when I'm coding!) if it is going to be null, or a
467        // zero-length-array.  You eventually just get used to typing this...
468        // I'm also getting used to typing 'final' everywhere when I know a variable just
469        // isn't going to change.
470
471        final boolean EXTRA_PACKAGE_NICKNAMES =
472            ((extraneousArgs != null) && (extraneousArgs.length > 0));
473
474        if (EXTRA_PACKAGE_NICKNAMES)
475        {
476            if (! pkgNickNamesPossible) throw printMenuHelp(options, extraneousArgs);
477
478            // These two lists, if non-empty, are going to cause an error-message to print, and the
479            // program exiting.
480
481            Stream.Builder<String> unrecognizedSwitchListB  = Stream.builder();
482            Stream.Builder<String> unrecognizedPackageListB = Stream.builder();
483
484            TOP1:
485            for (String arg : extraneousArgs)
486
487                if (arg.startsWith("-")) unrecognizedSwitchListB.accept(arg);
488
489                else
490                {
491                    for (BuildPackage bp : packageList) if (bp.nickName.equals(arg)) continue TOP1;
492                    unrecognizedPackageListB.accept(arg);
493                }
494
495            // *** NEW-ADDITION: Check this list too - This "Extra For-Loop" is only here to ensure
496            //     that the error message which is printed, includes all possible mistakes...
497
498            TOP2:
499            for (String arg : commandLine.getOptionValues(selectedOpt))
500            {
501                for (BuildPackage bp : packageList) if (bp.nickName.equals(arg)) continue TOP2;
502                unrecognizedPackageListB.accept(arg);
503            }
504
505            // *** END-NEW-ADDITION: The "unrecognizedPackageListB" also includes the Package
506            //     Nick-Names that Apache-CLI knows about...
507
508            final String[] unrecognizedSwitches =
509                unrecognizedSwitchListB.build().toArray(String[]::new);
510
511            final String[] unrecognizedPackages =
512                unrecognizedPackageListB.build().toArray(String[]::new);
513
514            // Print an error message and exit if either of these lists are non-empty
515            if ((unrecognizedSwitches.length + unrecognizedPackages.length) > 0)
516                throw printMenuHelp(options, unrecognizedSwitches, unrecognizedPackages);
517        }
518
519
520        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
521        // Fill in this class' "Extra Switch Fields" (These are the non-main-menu Switches)
522        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
523        //
524        // All of these variables are declared, even though they are largely just exact copies of
525        // the public fields declared at the top of this class - BECAUSE THOSE FIELDS ARE ALL
526        // DECLARED 'final' - Meaning they may only be assigned once in this giant constructor that
527        // I have written.
528        //
529        // Declaring them 'final' means they can also be declared 'public', and the end user can
530        // play with them to his heart's content, wihthout having the ability to screw anything up
531        // either!
532
533        boolean
534            NO_QUICK_BUILD_OPTION_SWITCH        = false,
535            INCLUDE_JAR_IN_CP                   = false,
536            SKIP_REMOVE_GCS_FILES_OPTION_SWITCH = false,
537            OVERRIDE_TOGGLE_USE_XLINT_SWITCH    = false,
538            OVERRIDE_TOGGLE_USE_XDIAGS_SWITCH   = false,
539            EARLY_DEVELOPMENT_SWITCH            = false;
540
541        // Get all processed / user-provided options
542        final Option[] optArr = commandLine.getOptions();
543
544        for (Option opt : optArr) switch (opt.getOpt())
545        {
546            case "NQB"  : NO_QUICK_BUILD_OPTION_SWITCH          = true; break;
547            case "JCP"  : INCLUDE_JAR_IN_CP                     = true; break;
548            case "SRG"  : SKIP_REMOVE_GCS_FILES_OPTION_SWITCH   = true; break;
549            case "TXL"  : OVERRIDE_TOGGLE_USE_XLINT_SWITCH      = true; break;
550            case "TXD"  : OVERRIDE_TOGGLE_USE_XDIAGS_SWITCH     = true; break;
551            case "IEDP" : EARLY_DEVELOPMENT_SWITCH              = true; break;
552            // default  : continue;
553        }
554
555        this.INCLUDE_JAR_IN_CP          = INCLUDE_JAR_IN_CP;
556        this.SKIP_REMOVE_GCS_FILES      = SKIP_REMOVE_GCS_FILES_OPTION_SWITCH;
557        this.INCLUDE_EARLY_DEV_PACKAGES = EARLY_DEVELOPMENT_SWITCH;
558
559        // Only Main-Menu Option "Complete-Build-1" allows for writing to the "Release GCS Bucket"
560        this.toReleaseOrDeveloper = this.MENU_CHOICE.equals("cb1");
561
562        // Only Main-Menu Option "#4 - TAR & JAR" accepts the "JARONLY" argument
563        final String optVal = commandLine.getOptionValue(selectedOpt);
564        this.JAR_ONLY = (optVal != null) && optVal.equals("JARONLY");
565
566        this.QUICKER_BUILD =
567                (this.MENU_CHOICE.equals("cb1"))
568            ||  (this.MENU_CHOICE.equals("cb2"))
569            ||  (this.MENU_CHOICE.equals("cb3"))
570            ? false
571            : (! NO_QUICK_BUILD_OPTION_SWITCH);
572
573        this.GCS_DIR = this.toReleaseOrDeveloper
574            ? GCS_DIR_RELEASE
575            : GCS_DIR_DEV;
576
577        this.OVERRIDE_TOGGLE_USE_XLINT_SWITCH   = OVERRIDE_TOGGLE_USE_XLINT_SWITCH;
578        this.OVERRIDE_TOGGLE_USE_XDIAGS_SWITCH  = OVERRIDE_TOGGLE_USE_XDIAGS_SWITCH;
579
580
581        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
582        // More Error-Checking
583        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
584        //
585        // These simple error-checks are simply trying to inform the user when particular flags
586        // may or may not be applied.  If, for instance, the user requested Main-Menu Option #4
587        // - Tar & Jar Files - then also providing the switch -SRG would simply be an error.  The
588        // value of these error-checks is that, when they are more rigorous, the error/help 
589        // explanations that are spit-out in their faces make explaining what any of this doing
590        // (at all), just that much easier to explain.
591    
592        if (this.INCLUDE_JAR_IN_CP)
593
594            if (! MENU_CHOICE.equals("1")) throw printMenuHelp(
595                options,
596                "The " + BRED + "--putJarInCP" + RESET + " (-JCP) may only be used alongside " +
597                "Main-Menu Option " + BRED + "--javac" + RESET + " (-1)"
598            );
599
600        // For -NQB, ONLY Main-Menu Options: -pb1 & -pb2, may receive this switch.
601        if (NO_QUICK_BUILD_OPTION_SWITCH) 
602
603            if (StrCmpr.equalsNAND(MENU_CHOICE, "pb1", "pb2")) throw printMenuHelp(
604                options,
605                "The " + BRED + "--noQuickBuild" + RESET + " (-NQB) switch may only be applied " +
606                "to the Partial-Build Menu-Options " + BRED + "--partialAll" + RESET + " (-pb1) " +
607                "and " + BRED + "--partialSome" + RESET + " (-pb2)"
608            );
609
610        // NOTE: Here, we need We DO NOT NEED TO TEST the value that was assigned to
611        //       this.SKIP_REMOVE_GCS_FILES.  We need to check the value indicating whether or not
612        //       the user passed the switch on the Command-Line.
613
614        if (SKIP_REMOVE_GCS_FILES_OPTION_SWITCH)
615
616            if (StrCmpr.equalsNAND(MENU_CHOICE, "cb2", "cb3")) throw printMenuHelp(
617                options,
618                "The Command-Switch " + BRED + "--skipRemoveGCSFiles" + RESET + " (-SRG) may " +
619                "only be used in combination with Main-Menu Option " + BRED + "`--buildDevSMA`" +
620                RESET + " (-cb2) or " + BRED + "`--buildDev`" + RESET + " (-cb3)"
621            );
622
623        if (this.OVERRIDE_TOGGLE_USE_XLINT_SWITCH || this.OVERRIDE_TOGGLE_USE_XDIAGS_SWITCH)
624
625            if (! MENU_CHOICE.equals("1")) throw printMenuHelp(
626                options,
627                "Switches " + BRED + "--toggleDefaultXlint" + RESET + " (-TXL) and " + BRED +
628                "--toggleDefaultXdiags" + RESET + " (-TXD) may only be used in combination with " +
629                "Main-Menu Option " + BRED + "`--javac`" + RESET + " (-1)"
630            );
631
632        if (this.INCLUDE_EARLY_DEV_PACKAGES)
633
634            if (StrCmpr.equalsXOR(MENU_CHOICE, "4", "6", "7", "cb1")) throw printMenuHelp(
635                options,
636                "The Command-Switch " + BRED + "--includeEarlyDev" + RESET + " (-IEDP) may " +
637                "only be used in combination with some of the Main-Menu Options:\n" +
638                "-1, -2, -3, -5, -8, -cb2, -cb3, -pb1, -pb2"
639            );
640
641
642
643        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
644        // Handle #1 & #21 - Package-NickNames / User-Specified Package-List
645        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
646        // 
647        // If the Main-Menu Option is followed by a list of String-Tokens, the only possible 
648        // presumption is that they are a list of package nick-names (or Option #4, the "JARONLY")
649        // String-Token Switch.
650        //
651        // Only Options #1 and #21 are allowed to accept the list of package nick-names
652
653        final String[]  nickNames           = commandLine.getOptionValues(selectedOpt);
654        final boolean   nickNamesProvided   = (nickNames != null) && (nickNames.length > 0);
655
656        // This error would (should !) automatically caught by Apache, and wouldnever reach this
657        // point.  This is just a variant of 'assert', and it's staying here for now.
658
659        if (nickNamesProvided && (! pkgNickNamesPossible)) throw new UnreachableError();
660
661        // A temporary list that is needed to merge the two different sources of Package Nick-Name
662        // arguments.  Since I simply cannot get Apache's Option-Argument thing to work with a
663        // configuration of ZERO, ONE or MANY Option-Arguments, I have to presume that it isn't
664        // possible.  As such, the source of Package Nick-Names are the "getOptionValues" **AND**
665        // **ALSO** the Extraneous-Options too!
666        //
667        // Merge those two lists, and then use that merged list as the package nick-names list.
668        // Remember that only options "1" (javac) and "pb2" (partial-build 2) even accept package
669        // nick-names lists.  All of the others do not accept any arguments at all.
670
671        final ArrayList<String> l = new ArrayList<>();
672
673        if (nickNamesProvided)
674            for (String pkgNickName : nickNames) l.add(pkgNickName);
675
676        if (EXTRA_PACKAGE_NICKNAMES)
677            for (String pkgNickName : extraneousArgs) l.add(pkgNickName);
678
679        this.userProvidedNickNames = nickNamesProvided
680            ? new ReadOnlyArrayList<String>(l)
681            : null;
682
683        this.userSpecifiedPackages = nickNamesProvided
684            ? BuildPackage.nickNameArgVPackages(packageList, this.userProvidedNickNames)
685            : null;
686    }
687
688
689    // ********************************************************************************************
690    // ********************************************************************************************
691    // Help-Menu Printing
692    // ********************************************************************************************
693    // ********************************************************************************************
694
695
696    // This happens when the Apache.CLI finds a Switch-Error
697    private UnreachableError printMenuHelp(Options options, ParseException pe)
698    {
699        printMenuHelp(options);
700        System.out.println(pe.getMessage());
701        System.exit(1);
702        return null; // javac is too noisy
703    }
704
705    private UnreachableError printMenuHelp(Options options, String explanation)
706    {
707        printMenuHelp(options);
708        System.out.println(explanation);
709        System.exit(1);
710        return null; // javac is too noisy
711    }
712
713    // This Error-Message is printed/written when the user failed to provide at least one of the
714    // Main-Menu Switch-Options.
715
716    private UnreachableError printMenuHelp(Options options, MissingOptionException moe)
717    {
718        printMenuHelp(options);
719
720        System.out.println(
721            BRED + "You must select exactly one of the primary Build Options:\n\n" + RESET +
722            BYELLOW + "    Build-Step Options:\n" + RESET +
723            "        -1 <OPTIONAL-PKG-LIST> | -2 | -3 | -4 <JARONLY> | -5 | -6 | -7 | -8 |\n\n" +
724            BYELLOW + "    Complete-Build Options:\n" + RESET +
725            "        -cb1 | -cb2 | -cb3 |\n\n" +
726            BYELLOW + "    Partial-Build Options:\n" + RESET +
727            "        -pb1 | -pb2 <OPTIONAL-PKG-LIST> |\n\n" +
728            BYELLOW + "    Links-Chcker Build:\n" + RESET +
729            "        -LC\n\n" +
730            BCYAN + "Additional switches [-JCP, -NQB, -SRG, -TXD, -TXL, -IEDP], may also be added\n" + RESET
731        );
732
733        System.exit(1);
734        return null; // javac is too noisy
735    }
736
737    private UnreachableError printMenuHelp(Options options, String[] unrecognizedSwitches)
738    {
739        printMenuHelp(options);
740
741        System.out.println(
742            "You have passed one or more spurious or unrecognized options:\n" +
743            BRED + "Unknown Command-Line Switches Include: " + RESET +
744            '[' + String.join(", ", unrecognizedSwitches) + ']' + '\n'
745        );
746
747        System.exit(1);
748        return null; // javac is too noisy
749    }
750
751    private UnreachableError printMenuHelp
752        (Options options, String[] unrecognizedSwitches, String[] unrecognizedPackages)
753    {
754        printMenuHelp(options);
755
756        if (unrecognizedSwitches.length > 0) System.out.println(
757            "You have passed one or more spurious or unrecognized options:\n" +
758            BRED + "Unknown Command-Line Switches: " + RESET +
759            '[' + String.join(", ", unrecognizedSwitches) + ']' + '\n'
760        );
761
762        if (unrecognizedPackages.length > 0) System.out.println(
763            "You have passed one or more unrecognized Package Nick-Names:\n" +
764            BRED + "Unknown Package Nick-Names: " + RESET +
765            '[' + String.join(", ", unrecognizedPackages) + ']' + '\n'
766        );
767
768        System.exit(1);
769        return null; // javac is too noisy
770    }
771
772    // This is the "printMenuHelp" portion of the output-printer that is constant / consisten for
773    // all of the previous print-menu help-functions above.
774
775    private static String descriptionCommandLine = 
776         "java MyBuildImpl -1 <PKG-LIST> | -2 | ... | -8 | -cb1 | -cb2 | -cb3 | -pb1 | " +
777        "-pb2 <PKG-LIST> | -LC\n" +
778    "    [-JCP, -NQB, -SRG, -TXD, -TXL, -IEDP]";
779
780    private static void printMenuHelp(Options options)
781    {
782        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
783        // Leave this here, I may try to bring this back one day, just not right now
784        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
785
786        System.out.println(
787            // BCYAN + "Old Build Menu:\n" + RESET +
788            HAND_TYPED_MAN_PAGE
789            // BCYAN + "\nApache Build Menu:\n" + RESET
790        );
791
792        /*
793        HelpFormatter hf = new HelpFormatter();
794        hf.setWidth(115);
795        hf.setLeftPadding(6);
796        hf.setOptionComparator(CLI::compareOptions);
797        */
798
799        // Playing with these.  They are a little silly.  Leave this here, just to remember what
800        // Apache.CLI even does!
801        //
802        // hf.setLongOptPrefix("^");
803        // hf.setLongOptSeparator("#");
804        // hf.setOptPrefix("@");
805        // hf.setSyntaxPrefix("!!");
806    
807        // hf.printHelp(descriptionCommandLine, options);
808
809        // System.out.println();
810    }
811
812    // This long-winded thing is, apparently, the only way I could get Apache.CLI to order the
813    // Menu-Options using the "HelpFormatter" above.  This is a java.util.Comparator
814    // Function-Pointer that actually "sorts" the Menu-Options for the Menu-Options-Printer method
815    // directly above.
816    //
817    // It sorts them to, sort of, emphasize, the Main-Menu Mandatory Options versus the
818    // Optional-Options.  These "Optional-Options" are the little "Extra-Switches" listed at the
819    // very end of the Menu.
820
821    private static int compareOptions(Option o1, Option o2)
822    {
823        final String name1          = o1.getOpt();
824        final String name2          = o2.getOpt();
825
826        // The first 8 Build-Steps and 3 Complete-Build-Steps would automatically be properly
827        // sorted at the TOP OF THE LIST - using the Nature-Comparison-Order ANYWAY!  There is no
828        // need to "subvert" the Natural-Sort-Order for Steps 1..8 and cb1, cb2 or cb3.  They are 
829        // already going to be sorted at the top of the list anyways, without any sort-order
830        // intervention.
831
832        final boolean naturalSort1  = (name1.length() == 1) || name1.startsWith("cb");
833        final boolean naturalSort2  = (name2.length() == 1) || name2.startsWith("cb");
834
835        if (naturalSort1 || naturalSort2) return name1.compareToIgnoreCase(name2);
836
837        // The two Partial-Build Main-Menu Options' Natural-Sort Order would innappropriately place
838        // them further down the Main-Menu than I would like them to be placed.  Therefore, this 
839        // comparator has to "intervene" and make sure they are next on the Comparitor's sort /
840        // return list - located DIRECTLY AFTER the three -cb (Complete-Build) Menu-Options.
841
842        final boolean pb1 = name1.startsWith("pb");
843        final boolean pb2 = name2.startsWith("pb");
844
845        if (pb1 && pb2) return name1.compareToIgnoreCase(name2);
846
847        if (pb1) return -1;
848        if (pb2) return 1;
849
850        // The last intervention is to place the "Links Check" Menu-Option DIRECTLY AFTER the
851        // "Partial-Build" (-pb1 & -pb2) Menu-Options.
852
853        final boolean lc1 = name1.startsWith("LC");
854        final boolean lc2 = name2.startsWith("LC");
855
856        if (lc1) return -1;
857        if (lc2) return 1;
858
859        // These are the "Extra-Switches" which add some features to make Life easier for me.
860        // These are placed at the VERY END of the menu, and their individual Menu-Placement / 
861        // Sort-Order is completely irrelevant to me.  So as long as these are at the end of the
862        // list, from that point, they may be placed in their Natural-Order Locations.
863
864        return name1.compareToIgnoreCase(name2);
865    }
866
867    static final String HAND_TYPED_MAN_PAGE =
868        BYELLOW + "\n\tJava Compiler Step:" + RESET +
869        "\n\t\t-1  -> Compile [Optional: Package-Nicknames)" +
870
871        BYELLOW + "\n\n\tBuild Steps:" + RESET +
872        "\n\t\t-2   -> JavaDoc" +
873        "\n\t\t-3   -> Upgrader" +
874        "\n\t\t-4   -> Build TAR's & JAR [Optional: 'JARONLY']" +
875        "\n\t\t-5   -> Copy to GCS: 'javadoc" + File.separator + "' directory" +
876        "\n\t\t-6   -> Copy to GCS: '.tar' and '.jar' files" +
877        "\n\t\t-7   -> Copy to GCS: logs" +
878        "\n\t\t-8   -> GSUTIL.SMA(GCS_DIR + \"**\", 150);" +
879
880        BYELLOW + "\n\n\tComplete Build (Composite Processes):" + RESET +
881        "\n\t\t-cb1 -> STEPS 2 through 7     [Pushes-To: GCP-Release]" +
882        "\n\t\t-cb2 -> STEPS 2 through 8     [Pushes-To: GCP-Developer] w/ Set-Max-Age " +
883        "\n\t\t-cb3 -> STEPS 2 through 7     [Pushes-To: GCP-Developer]" +
884
885        BYELLOW + "\n\n\tPartial-Debuging Build, (Push-To: GCP-Developer only):" + RESET +
886        "\n\t\t-pb1 -> SETPS 2 and 3         [Doc Only]" +
887        "\n\t\t-pb2 -> STEPS 2, 3, 5, and 8  [Doc, Sync & Max-Age. Optional: Package-Nicknames]" +
888
889        BYELLOW + "\n\n\tLinks-Checker:" + RESET +
890        "\n\t\t-LC  -> STEPS 2 and 3, WITH-LINKS-CHECK" +
891
892        BYELLOW + "\n\n\tAdditional Side Options:" + RESET +
893        "\n\t\t-JCP,--putJarInCP             " + descriptionNQB +
894        "\n\t\t-NQB,--noQuickBuild           " + descriptionJCP +
895        "\n\t\t-SRG,--skipRemoveGCSFiles     " + descriptionSRG +
896        "\n\t\t-TXD,--toggleDefaultXdiags    " + descriptionTXD +
897        "\n\t\t-TXL,--toggleDefaultXlint     " + descriptionTXL +
898        "\n\t\t-IEDP,--includeEarlyDev       " + descriptionIEDP +
899
900        "\n";
901}