1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
package Torello.Java.Build;

import Torello.JavaDoc.Upgrade;

import Torello.Java.Verbosity;
import Torello.Java.StrCmpr;
import Torello.Java.EmptyListException;
import Torello.Java.WritableDirectoryException;
import Torello.Java.GSUTIL; // Needed for a javadoc '@link'

import Torello.Java.ReadOnly.ReadOnlyList;

import Torello.HTML.Tools.Images.IF;

import java.io.File;
import java.io.FileNotFoundException;

import java.util.Objects;
import java.util.regex.Pattern;

import java.util.function.Consumer;
import java.util.function.Predicate;

/**
 * This class provides a list of fields, all of which may be modified and configured, for providing
 * any needed settings to class {@link Builder}.
 * 
 * <BR /><BR />Use the {@code public} constructor offered by this class, and then set and change
 * the values of any of the Configuration-Fields.  Once this class Configuration-Fields have been
 * appropriately set to contain your project's data, pass the instance to the {@link Builder} class
 * only constructor (along with an argv {@code String[]}-Array).
 * 
 * <BR /><BR />This will inform the {@code Builder}-Constructor of all of your nedded settings, and
 * once built, will make running your project's build script very easy.
 */
public final class Config
{
    // ********************************************************************************************
    // ********************************************************************************************
    // Validator Constants
    // ********************************************************************************************
    // ********************************************************************************************


    /**
     * Project-Name validation {@code String}.  The value passed to configuration field/parameter
     * must match this regular expression.  This is used in this class {@link #validate()} method.
     * 
     * @see #PROJECT_NAME
     * @see Builder#PROJECT_NAME
     * @see #validate()
     */
    public static final Predicate<String> projectNameValidator =
        Pattern.compile("^\\w[\\w\\d]*$").asPredicate();


    // ********************************************************************************************
    // ********************************************************************************************
    // Configuration Instance Fields
    // ********************************************************************************************
    // ********************************************************************************************


    /**
     * To-Do, Explain This
     * @see Builder#HOME_DIR
     */
    public String HOME_DIR = null;

    /**
     * The File-System Directory-Location where Java-Doc Output is sent for storage.  Also the 
     * directory that the Upgrader searches to retrieve and parse JavaDoc Files.
     *  
     * @see Builder#LOCAL_JAVADOC_DIR
     */
    public String LOCAL_JAVADOC_DIR = "javadoc" + java.io.File.separator;

    /**
     * A simple name for this Project.  Name must pass the {@link #projectNameValidator}
     * Regular-Expression.
     * 
     * @see Builder#PROJECT_NAME
     * @see #projectNameValidator
     */
    public String PROJECT_NAME = null;

    /**
     * The Major Version Number of the Current Build.  This class {@link #validate()} method checks
     * to ensure that this field contains a positive integer / byte value.
     * 
     * @see Builder#VERSION_MAJOR_NUMBER
     */
    public byte VERSION_MAJOR_NUMBER = 1;

    /**
     * The Minor Version Number of the Current Build.  This class {@link #validate()} method checks
     * to ensure that this field contains a positive integer / byte value.
     * 
     * @see Builder#VERSION_MINOR_NUMBER
     */
    public byte VERSION_MINOR_NUMBER = 0;

    /**
     * The full or partial Path-Name of the {@code 'javac'} binary to use for compiling this
     * project.  This parameter / field may not be null, or a {@code NullPointerException} will
     * throw.
     * 
     * <BR /><BR /><B CLASS=JDDescLabel>Default Setting:</B>
     * 
     * <BR />The default setting for this Configuration-Field is just the command name, which
     * is to presume that the {@code PATH} environment-variable has been set to facilitate finding
     * {@code 'javac'}
     * 
     * @see Builder#JAVAC_BIN
     */
    public String JAVAC_BIN = "javac";

    /**
     * The full or partial Path-Name of the {@code 'javac'} binary to use for compiling this
     * project.  This parameter / field may not be null, or a {@code NullPointerException} will
     * throw.
     * 
     * <BR /><BR /><B CLASS=JDDescLabel>Default Setting:</B>
     * 
     * <BR />The default setting for this Configuration-Field is just the command name, which
     * is to presume that the {@code PATH} environment-variable has been set to facilitate finding
     * {@code 'javadoc'}.
     * 
     * @see Builder#JAVADOC_BIN
     */
    public String JAVADOC_BIN = "javadoc";

    /**
     * The number to provide to the {@code --release} switch to {@code javac}.  This number may be
     * set to any negative value, and it will prevent the {@code Builder} from using this switch at
     * all
     * 
     * @see Builder#JAVA_RELEASE_NUM_SWITCH
     */
    public byte JAVA_RELEASE_NUM_SWITCH = -1;

    /**
     * Currently unused, but originally used to indicate the {@code javadoc} version being used
     * @see Builder#JAVADOC_VER
     */
    public byte JAVADOC_VER = 11;

    /**
     * If the <B>Stage 3</B> {@link Upgrade}-Process utilizes a favicon on its Web-Pages, then this
     * field may be assigned to the location on disk where that favicon is located.
     * 
     * <BR /><BR />When this field contains a non-null Image-File, that image will be copied (after
     * upgrade has completed) directly to the {@code 'javadoc'} output directory.  When this field
     * is left null, it will be ignored and no favicon image is copied.
     * 
     * @see Builder#FAVICON
     */
    public String FAVICON = null;

    /**
     * Source-Directory to be used as root directory for archiving
     * @see Builder#TAR_SOURCE_DIR
     */
    public String TAR_SOURCE_DIR = null;

    /**
     * The Google Cloud Platform Storage-Bucket into which jar, log &amp; documentation files will
     * be copied by this Build-Tool.  This Storage-Bucket Directory is intended for use with a
     * boiler-plate &amp; "still under development" {@code URL}.
     * 
     * <BR /><BR />If this parameter remains null, several of the user menu options will be
     * removed, because several are designed for coying data to the Developer Site.  When it is
     * null such options are removed, and this Configuration-Field is ignored.
     * 
     * @see Builder#GCS_DIR_DEV
     * @see #GCS_DIR_RELEASE
     * @see Builder#GCS_DIR_RELEASE
     */
    public String GCS_DIR_DEV = null;

    /**
     * Another Google Cloud Platform Storage-Bucket for storing and providing jar, log &amp;
     * Code-Documentation Files.
     * 
     * <BR /><BR />Similary to Configuration-Field {@link #GCS_DIR_DEV}, this parameter may also be
     * null, and when it is it will be ignored, but a few User-Interface Menu-Options will not be 
     * present at the Build-Menu.
     * 
     * @see Builder#GCS_DIR_RELEASE
     * @see #GCS_DIR_DEV
     * @see Builder#GCS_DIR_DEV
     */
    public String GCS_DIR_RELEASE = null;

    /**
     * Informs the Build-Tool whether or not the {@link GSUTIL} method for making the output
     * directory content public needs to be run.  This {@code boolean} controls whether or not
     * {@code javadoc} content synchronized / copied to the {@link #GCS_DIR_DEV} is assigned the
     * public read-only access level.
     * 
     * <BR /><BR />When {@link #GCS_DIR_DEV} is left null, thi flag is ignored.
     * 
     * <BR /><BR />There is also a {@code boolean} setting for the {@link #GCS_DIR_RELEASE} flag.
     * 
     * <BR /><BR />The default setting for both of these flags is {@code FALSE}.
     * 
     * @see #GCS_DIR_DEV
     * @see Builder#GCS_DIR_DEV
     * @see #RUN_MAKE_PUBLIC_RELEASE
     */
    public boolean RUN_MAKE_PUBLIC_DEV = false;

    /**
     * Informs the Build-Tool whether or not the {@link GSUTIL} method for making the output
     * directory content public needs to be run.  This {@code boolean} controls whether or not
     * {@code javadoc} content synchronized / copied to the {@link #GCS_DIR_RELEASE} is assigned
     * the public read-only access level.
     * 
     * <BR /><BR />When {@link #GCS_DIR_RELEASE} is left null, thi flag is ignored.
     * 
     * <BR /><BR />There is also a {@code boolean} setting for the {@link #GCS_DIR_DEV} flag.
     * 
     * <BR /><BR />The default setting for both of these flags is {@code FALSE}.
     * 
     * @see #GCS_DIR_RELEASE
     * @see Builder#GCS_DIR_RELEASE
     * @see #RUN_MAKE_PUBLIC_DEV
     */
    public boolean RUN_MAKE_PUBLIC_RELEASE = false;

    /**
     * A Storage-Bucket on Google Cloud Platform for saving the generated backup {@code '.tar'}
     * File.  This Configuration-Field may remain null, and if it is it will be silently ignored.
     * In this case, the generated backup {@code '.tar'} File will not be copied onto any Google
     * Cloud Platform Storage-Buckets.
     * 
     * @see Builder#BACKUP_TAR_FILE_GCS_DIR
     */
    public String BACKUP_TAR_FILE_GCS_DIR = null;

    /**
     * This optional configuration field is optional, and may be used to move / relocate the
     * {@code '.jar'} File after it's built by this Build-Processor.  If there is a diretory or
     * location on the File-System into which the {@code '.jar'} should be moved, then provide that
     * directory's name here.
     * 
     * <BR /><BR />When this field is left null, it will be silently ignored; furthermore, the
     * {@code '.jar'} File which the Build-Processor creates will be left in the Current Working
     * Directory.
     */
    public String JAR_FILE_MOVE_DIR = null;

    /**
     * The directory location where log information will be saved.  This configuration field may
     * not be null, or a {@code NullPointerException} will throw.
     */
    public String LOG_DIR = null;

    /**
     * The <B>Stage 3</B> {@link Upgrade} instance to invoke.  This parameter-field configuration
     * may not be null, or a {@code NullPointerException} will throw.
     */
    public Upgrade upgrader = null;

    /**
     * The script invoked <B STYLE='color: red'><I>before</I></B> the <B>Stage 3</B> call to
     * {@link Upgrade}.  This configuration may be assigned null, and in such cases, no script will
     * be invoked before the upgrader runs.
     * 
     * @see Builder#preUpgraderScript
     */
    public UpgradeProcessor preUpgraderScript = null;

    /**
     * The script invoked <B STYLE='color: red'><I>after</I></B> the <B>Stage 3</B> call to
     * {@link Upgrade}.  This configuration may be assigned null, and in such cases, no script will
     * be invoked before the upgrader runs.
     * 
     * @see Builder#postUpgraderScript
     */
    public UpgradeProcessor postUpgraderScript = null;

    /**
     * For Stage 1, the Java-Compiler Stage, this Configuration-Field may be assigned any
     * switch-{@code String's}, and they will be passed to the {@code javac} invocation.
     * 
     * <BR /><BR />This field's value may be left null, or assigned a zero-length 
     * {@code String[]}-Array and, in both cases, it shall be ignored.
     * 
     * @see Builder#extraSwitchesJAVAC
     */
    public String[] extraSwitchesJAVAC = null;

    /**
     * For Stage 2, the {@code javadoc} Stage, this Configuration-Field may be assigned any
     * switch-{@code String's}, and they will be passed to the {@code javadoc} invocation.
     * 
     * <BR /><BR />This field's value may be left or null, or assigned a zero-length 
     * {@code String[]}-Array, and, in both cases, it shall be ignored.
     * 
     * @see Builder#extraSwitchesJAVADOC
     */
    public String[] extraSwitchesJAVADOC = null;

    /**
     * The list of Java Packages to compile.  This list may be neither empty nor null, or an
     * exception will throw.
     * 
     * @see Builder#packageList
     */
    public BuildPackage[] packageList = null;

    /**
     * The Root Class-File Directories to use with {@code 'javac'}.  This is used to generate the
     * value passed to the {@code -cp} switch passed when <B>Stage 1</B> invokes the Java-Compiler.
     * This parameter / field may be empty or null, and if it is, it will be ignored.
     */
    public String[] CLASS_PATH = null;

    /**
     * This allows for specifying a list of files to manually add into the {@code '.jar'} that is
     * built by this package.
     * 
     * <BR /><BR /><B CLASS=JDDescLabel>Constructor Call:</B>
     * 
     * <BR />Because it isn't necessary to include any "Extra Files" into the {@code '.jar'}, there
     * is some likelihood that this Configuration-Field wouldn't ever be needed / used.  Make sure
     * to call the {@link JarInclude} constructor, first, to build an instance of this Config-Field
     * before attempting to use it.  Notice that it is initially initialized to null!
     * 
     * <BR /><BR /><B CLASS=JDDescLabel>Common Usess:</B>
     * 
     * <BR />Java-HTML has quite a few Data-Files that are inserted into the Jar-File, manually,
     * using this class.  There may also be the need to insert {@code META-INF/} directory files
     * that name things like Annotation-Processor locations, taglets, or other Java Services.
     * 
     * @see Builder#jarIncludes
     */
    public JarInclude jarIncludes = null;

    /**
     * Requests that when {@code 'javac'} is invoked, the switch {@code -Xlint:all,-processing} is
     * included in the command-line invocation.
     * 
     * @see Builder#USE_XLINT_SWITCH
     */
    public boolean USE_XLINT_SWITCH = true;

    /**
     * Requests that when {@code 'javac'} is invoked, the switch {@code -Xdiags:verbose} is
     * included in the command-line invocation.
     * 
     * @see Builder#USE_XDIAGS_SWITCH
     */
    public boolean USE_XDIAGS_SWITCH = true;

    /**
     * JavaDoc Frames were a true majesty that should never have been eliminated.  If you would
     * prefer to remove them, or you are using JDK 21+ which no longer accepts the {@code --frames}
     * switch, then set this Configuration-Field to {@code FALSE}.
     * 
     * @see Builder#NO_JAVADOC_FRAMES_SWITCH
     */
    public boolean NO_JAVADOC_FRAMES_SWITCH = false;


    // ********************************************************************************************
    // ********************************************************************************************
    // Configuration Instance Fields
    // ********************************************************************************************
    // ********************************************************************************************


    /**
     * Creates an instance of this class, with all default values assigned to the parameter /
     * fields.
     * 
     * <BR /><BR />The intention is that an instance of this class will have its
     * Configuration-Fields property assigned, according to the needs of the Project being built.
     * Afterwards, the {@code 'Config'} instance may be passed to the {@code 'Builder'}-Constructor
     * and then that instance may be used to build the project.
     * 
     * @see Builder#Builder(Config, String...)
     * @see Builder#build()
     */
    public Config() { }


    // ********************************************************************************************
    // ********************************************************************************************
    // Configuration Instance Fields
    // ********************************************************************************************
    // ********************************************************************************************


    /**
     * Used by class {@link Builder} to validate all user-provided data on a best efforts basis.
     * 
     * @throws NullPointerException If any of the configuration parameter/fields listed here have
     * been assigned null
     * 
     * <BR /><UL CLASS=JDUL>
     * <LI>{@link #LOCAL_JAVADOC_DIR}</LI>
     * <LI>{@link #PROJECT_NAME}</LI>
     * <LI>{@link #JAVAC_BIN}</LI>
     * <LI>{@link #JAVADOC_BIN}</LI>
     * <LI>{@link #TAR_SOURCE_DIR}</LI>
     * <LI>{@link #LOG_DIR}</LI>
     * <LI>{@link #upgrader}</LI>
     * <LI>{@link #packageList}</LI>
     * </UL>
     * 
     * <BR />This exception will also be thrown if these lists contain any null-valued
     * entries:
     * <BR /><UL CLASS=JDUL>
     * <LI>{@link #packageList}</LI>
     * <LI>{@link #CLASS_PATH}</LI>
     * </UL>
     * 
     * @throws IllegalArgumentException If any of these configuration fields were assigned a 
     * negative number:
     * <BR /><UL CLASS=JDUL>
     * <LI>{@link VERSION_MAJOR_NUMBER}</LI>
     * <LI>{@link VERSION_MINOR_NUMBER}</LI>
     * <LI>{@link JAVADOC_VER}</LI>
     * </UL>
     * 
     * <BR />This exception will also be thrown if:
     * <BR /><UL CLASS=JDUL>
     * <LI> {@link #PROJECT_NAME} does not pass the {@link #projectNameValidator} test.</LI>
     * </UL>
     * 
     * @throws FileNotFoundException This exception will throw if either of these fields are not
     * assigned a valid File-System Directory-Name:
     * <BR /><UL CLASS=JDUL>
     * <LI> {@link #FAVICON}</LI>
     * <LI> {@link #TAR_SOURCE_DIR}</LI>
     * </UL>
     * 
     * @throws UnrecognizedImageExtException If the {@link #FAVICON} Image-Format cannot be 
     * properly "guessed" by class {@link IF}.
     * 
     * @throws EmptyListException If {@link #packageList} has been assigned an empty list.
     * 
     * @throws WriteableDirectoryException If parameter {@link #JAR_FILE_MOVE_DIR} is assigned a 
     * non-null value, but the {@code String} assigned it does not point to a File-System
     * directory, or that directory is not writeable.
     * 
     * <BR /><BR />Also throws if {@link #LOG_DIR} is not a valid directory-name, that allows
     * write-access.
     */
    public final void validate() throws java.io.FileNotFoundException
    {
        Objects.requireNonNull(LOCAL_JAVADOC_DIR,   M1 + "LOCAL_JAVADOC_DIR" + M2);
        Objects.requireNonNull(PROJECT_NAME,        M1 + "PROJECT_NAME" + M2);
        Objects.requireNonNull(JAVAC_BIN,           M1 + "JAVAC_BIN" + M2);
        Objects.requireNonNull(JAVADOC_BIN,         M1 + "JAVADOC_BIN" + M2);
        Objects.requireNonNull(TAR_SOURCE_DIR,      M1 + "TAR_SOURCE_DIR" + M2);
        Objects.requireNonNull(LOG_DIR,             M1 + "LOG_DIR" + M2);
        Objects.requireNonNull(upgrader,            M1 + "upgrader" + M2);
        Objects.requireNonNull(packageList,         M1 + "packageList" + M2);

        if (! projectNameValidator.test(PROJECT_NAME)) throw new IllegalArgumentException
            (M1 + "PROJECT_NAME] was not passed a valid name.");

        if (VERSION_MAJOR_NUMBER < 0) throw new IllegalArgumentException
            (M1 + "VERSION_MAJOR_NUMBER" + M3 + VERSION_MAJOR_NUMBER);

        if (VERSION_MINOR_NUMBER < 0) throw new IllegalArgumentException
            (M1 + "VERSION_MINOR_NUMBER" + M3 + VERSION_MINOR_NUMBER);

        if (JAVADOC_VER < 0) throw new IllegalArgumentException
            (M1 + "JAVADOC_VER" + M3 + JAVADOC_VER);

        if (FAVICON != null)
        {
            File f = new File(FAVICON);

            if ((! f.exists()) || (! f.isFile())) throw new FileNotFoundException(
                "Field 'FAVICON' [" + FAVICON + "] points to a file that was not found, or " +
                "isn't a File"
            );

            IF.guessOrThrow(FAVICON);
        }

        if (packageList.length == 0) throw new EmptyListException
            (M1 + "packageList] was passed a list of size 0.");

        for (BuildPackage bp : packageList) if (bp == null) throw new NullPointerException
            (M1 + "packageList] contains a null value.");

        for (String p : CLASS_PATH) if (p == null) throw new NullPointerException
            (M1 + "CLASS_PATH] contains a null value.");

        File f = new File(TAR_SOURCE_DIR);
        if (! (f.exists() && f.isDirectory())) throw new FileNotFoundException
            (M1 + "] was not passed a vaild File-System Directory-Name.");

        if (JAR_FILE_MOVE_DIR != null)
        {
            WritableDirectoryException.check(JAR_FILE_MOVE_DIR);

            if (! JAR_FILE_MOVE_DIR.endsWith(File.separator))
                JAR_FILE_MOVE_DIR = JAR_FILE_MOVE_DIR + File.separator;
        }

        WritableDirectoryException.check(LOG_DIR);

        if (! LOG_DIR.endsWith(File.separator)) LOG_DIR = LOG_DIR + File.separator;
    }

    private static final String M1 = "The Parameter-Field Configuration [";
    private static final String M2 = "] was ultimately assigned null.";
    private static final String M3 = "] was passed a negative number: ";

    final String classPathStr(boolean INCLUDE_JAR_IN_CP, String JAR_FILE_NAME)
    {
        if (CLASS_PATH == null) return INCLUDE_JAR_IN_CP ? JAR_FILE_NAME : null;

        final String sep = StrCmpr.containsIgnoreCase(System.getProperty("os.name"), "win")
            ? ";"
            : ":";

        final StringBuilder sb  = new StringBuilder();

        for (int i=0; i < CLASS_PATH.length; i++) sb.append(((i==0) ? "" : sep) + CLASS_PATH[i]);

        if (INCLUDE_JAR_IN_CP) sb.append(sep + JAR_FILE_NAME);

        return sb.toString();
    }
}