001package Torello.Java.Build;
002
003import Torello.JavaDoc.Upgrade;
004
005import Torello.Java.Verbosity;
006import Torello.Java.StrCmpr;
007import Torello.Java.EmptyListException;
008import Torello.Java.WritableDirectoryException;
009import Torello.Java.GSUTIL; // Needed for a javadoc '@link'
010
011import Torello.Java.ReadOnly.ReadOnlyList;
012
013import Torello.HTML.Tools.Images.IF;
014
015import java.io.File;
016import java.io.FileNotFoundException;
017
018import java.util.Objects;
019import java.util.regex.Pattern;
020
021import java.util.function.Consumer;
022import java.util.function.Predicate;
023
024/**
025 * This class provides a list of fields, all of which may be modified and configured, for providing
026 * any needed settings to class {@link Builder}.
027 * 
028 * <BR /><BR />Use the {@code public} constructor offered by this class, and then set and change
029 * the values of any of the Configuration-Fields.  Once this class Configuration-Fields have been
030 * appropriately set to contain your project's data, pass the instance to the {@link Builder} class
031 * only constructor (along with an argv {@code String[]}-Array).
032 * 
033 * <BR /><BR />This will inform the {@code Builder}-Constructor of all of your nedded settings, and
034 * once built, will make running your project's build script very easy.
035 */
036public final class Config
037{
038    // ********************************************************************************************
039    // ********************************************************************************************
040    // Validator Constants
041    // ********************************************************************************************
042    // ********************************************************************************************
043
044
045    /**
046     * Project-Name validation {@code String}.  The value passed to configuration field/parameter
047     * must match this regular expression.  This is used in this class {@link #validate()} method.
048     * 
049     * @see #PROJECT_NAME
050     * @see Builder#PROJECT_NAME
051     * @see #validate()
052     */
053    public static final Predicate<String> projectNameValidator =
054        Pattern.compile("^\\w[\\w\\d]*$").asPredicate();
055
056
057    // ********************************************************************************************
058    // ********************************************************************************************
059    // Configuration Instance Fields
060    // ********************************************************************************************
061    // ********************************************************************************************
062
063
064    /**
065     * To-Do, Explain This
066     * @see Builder#HOME_DIR
067     */
068    public String HOME_DIR = null;
069
070    /**
071     * The File-System Directory-Location where Java-Doc Output is sent for storage.  Also the 
072     * directory that the Upgrader searches to retrieve and parse JavaDoc Files.
073     *  
074     * @see Builder#LOCAL_JAVADOC_DIR
075     */
076    public String LOCAL_JAVADOC_DIR = "javadoc" + java.io.File.separator;
077
078    /**
079     * A simple name for this Project.  Name must pass the {@link #projectNameValidator}
080     * Regular-Expression.
081     * 
082     * @see Builder#PROJECT_NAME
083     * @see #projectNameValidator
084     */
085    public String PROJECT_NAME = null;
086
087    /**
088     * The Major Version Number of the Current Build.  This class {@link #validate()} method checks
089     * to ensure that this field contains a positive integer / byte value.
090     * 
091     * @see Builder#VERSION_MAJOR_NUMBER
092     */
093    public byte VERSION_MAJOR_NUMBER = 1;
094
095    /**
096     * The Minor Version Number of the Current Build.  This class {@link #validate()} method checks
097     * to ensure that this field contains a positive integer / byte value.
098     * 
099     * @see Builder#VERSION_MINOR_NUMBER
100     */
101    public byte VERSION_MINOR_NUMBER = 0;
102
103    /**
104     * The full or partial Path-Name of the {@code 'javac'} binary to use for compiling this
105     * project.  This parameter / field may not be null, or a {@code NullPointerException} will
106     * throw.
107     * 
108     * <BR /><BR /><B CLASS=JDDescLabel>Default Setting:</B>
109     * 
110     * <BR />The default setting for this Configuration-Field is just the command name, which
111     * is to presume that the {@code PATH} environment-variable has been set to facilitate finding
112     * {@code 'javac'}
113     * 
114     * @see Builder#JAVAC_BIN
115     */
116    public String JAVAC_BIN = "javac";
117
118    /**
119     * The full or partial Path-Name of the {@code 'javac'} binary to use for compiling this
120     * project.  This parameter / field may not be null, or a {@code NullPointerException} will
121     * throw.
122     * 
123     * <BR /><BR /><B CLASS=JDDescLabel>Default Setting:</B>
124     * 
125     * <BR />The default setting for this Configuration-Field is just the command name, which
126     * is to presume that the {@code PATH} environment-variable has been set to facilitate finding
127     * {@code 'javadoc'}.
128     * 
129     * @see Builder#JAVADOC_BIN
130     */
131    public String JAVADOC_BIN = "javadoc";
132
133    /**
134     * The number to provide to the {@code --release} switch to {@code javac}.  This number may be
135     * set to any negative value, and it will prevent the {@code Builder} from using this switch at
136     * all
137     * 
138     * @see Builder#JAVA_RELEASE_NUM_SWITCH
139     */
140    public byte JAVA_RELEASE_NUM_SWITCH = -1;
141
142    /**
143     * Currently unused, but originally used to indicate the {@code javadoc} version being used
144     * @see Builder#JAVADOC_VER
145     */
146    public byte JAVADOC_VER = 11;
147
148    /**
149     * If the <B>Stage 3</B> {@link Upgrade}-Process utilizes a favicon on its Web-Pages, then this
150     * field may be assigned to the location on disk where that favicon is located.
151     * 
152     * <BR /><BR />When this field contains a non-null Image-File, that image will be copied (after
153     * upgrade has completed) directly to the {@code 'javadoc'} output directory.  When this field
154     * is left null, it will be ignored and no favicon image is copied.
155     * 
156     * @see Builder#FAVICON
157     */
158    public String FAVICON = null;
159
160    /**
161     * Source-Directory to be used as root directory for archiving
162     * @see Builder#TAR_SOURCE_DIR
163     */
164    public String TAR_SOURCE_DIR = null;
165
166    /**
167     * The Google Cloud Platform Storage-Bucket into which jar, log &amp; documentation files will
168     * be copied by this Build-Tool.  This Storage-Bucket Directory is intended for use with a
169     * boiler-plate &amp; "still under development" {@code URL}.
170     * 
171     * <BR /><BR />If this parameter remains null, several of the user menu options will be
172     * removed, because several are designed for coying data to the Developer Site.  When it is
173     * null such options are removed, and this Configuration-Field is ignored.
174     * 
175     * @see Builder#GCS_DIR_DEV
176     * @see #GCS_DIR_RELEASE
177     * @see Builder#GCS_DIR_RELEASE
178     */
179    public String GCS_DIR_DEV = null;
180
181    /**
182     * Another Google Cloud Platform Storage-Bucket for storing and providing jar, log &amp;
183     * Code-Documentation Files.
184     * 
185     * <BR /><BR />Similary to Configuration-Field {@link #GCS_DIR_DEV}, this parameter may also be
186     * null, and when it is it will be ignored, but a few User-Interface Menu-Options will not be 
187     * present at the Build-Menu.
188     * 
189     * @see Builder#GCS_DIR_RELEASE
190     * @see #GCS_DIR_DEV
191     * @see Builder#GCS_DIR_DEV
192     */
193    public String GCS_DIR_RELEASE = null;
194
195    /**
196     * Informs the Build-Tool whether or not the {@link GSUTIL} method for making the output
197     * directory content public needs to be run.  This {@code boolean} controls whether or not
198     * {@code javadoc} content synchronized / copied to the {@link #GCS_DIR_DEV} is assigned the
199     * public read-only access level.
200     * 
201     * <BR /><BR />When {@link #GCS_DIR_DEV} is left null, thi flag is ignored.
202     * 
203     * <BR /><BR />There is also a {@code boolean} setting for the {@link #GCS_DIR_RELEASE} flag.
204     * 
205     * <BR /><BR />The default setting for both of these flags is {@code FALSE}.
206     * 
207     * @see #GCS_DIR_DEV
208     * @see Builder#GCS_DIR_DEV
209     * @see #RUN_MAKE_PUBLIC_RELEASE
210     */
211    public boolean RUN_MAKE_PUBLIC_DEV = false;
212
213    /**
214     * Informs the Build-Tool whether or not the {@link GSUTIL} method for making the output
215     * directory content public needs to be run.  This {@code boolean} controls whether or not
216     * {@code javadoc} content synchronized / copied to the {@link #GCS_DIR_RELEASE} is assigned
217     * the public read-only access level.
218     * 
219     * <BR /><BR />When {@link #GCS_DIR_RELEASE} is left null, thi flag is ignored.
220     * 
221     * <BR /><BR />There is also a {@code boolean} setting for the {@link #GCS_DIR_DEV} flag.
222     * 
223     * <BR /><BR />The default setting for both of these flags is {@code FALSE}.
224     * 
225     * @see #GCS_DIR_RELEASE
226     * @see Builder#GCS_DIR_RELEASE
227     * @see #RUN_MAKE_PUBLIC_DEV
228     */
229    public boolean RUN_MAKE_PUBLIC_RELEASE = false;
230
231    /**
232     * A Storage-Bucket on Google Cloud Platform for saving the generated backup {@code '.tar'}
233     * File.  This Configuration-Field may remain null, and if it is it will be silently ignored.
234     * In this case, the generated backup {@code '.tar'} File will not be copied onto any Google
235     * Cloud Platform Storage-Buckets.
236     * 
237     * @see Builder#BACKUP_TAR_FILE_GCS_DIR
238     */
239    public String BACKUP_TAR_FILE_GCS_DIR = null;
240
241    /**
242     * This optional configuration field is optional, and may be used to move / relocate the
243     * {@code '.jar'} File after it's built by this Build-Processor.  If there is a diretory or
244     * location on the File-System into which the {@code '.jar'} should be moved, then provide that
245     * directory's name here.
246     * 
247     * <BR /><BR />When this field is left null, it will be silently ignored; furthermore, the
248     * {@code '.jar'} File which the Build-Processor creates will be left in the Current Working
249     * Directory.
250     */
251    public String JAR_FILE_MOVE_DIR = null;
252
253    /**
254     * The directory location where log information will be saved.  This configuration field may
255     * not be null, or a {@code NullPointerException} will throw.
256     */
257    public String LOG_DIR = null;
258
259    /**
260     * The <B>Stage 3</B> {@link Upgrade} instance to invoke.  This parameter-field configuration
261     * may not be null, or a {@code NullPointerException} will throw.
262     */
263    public Upgrade upgrader = null;
264
265    /**
266     * The script invoked <B STYLE='color: red'><I>before</I></B> the <B>Stage 3</B> call to
267     * {@link Upgrade}.  This configuration may be assigned null, and in such cases, no script will
268     * be invoked before the upgrader runs.
269     * 
270     * @see Builder#preUpgraderScript
271     */
272    public UpgradeProcessor preUpgraderScript = null;
273
274    /**
275     * The script invoked <B STYLE='color: red'><I>after</I></B> the <B>Stage 3</B> call to
276     * {@link Upgrade}.  This configuration may be assigned null, and in such cases, no script will
277     * be invoked before the upgrader runs.
278     * 
279     * @see Builder#postUpgraderScript
280     */
281    public UpgradeProcessor postUpgraderScript = null;
282
283    /**
284     * For Stage 1, the Java-Compiler Stage, this Configuration-Field may be assigned any
285     * switch-{@code String's}, and they will be passed to the {@code javac} invocation.
286     * 
287     * <BR /><BR />This field's value may be left null, or assigned a zero-length 
288     * {@code String[]}-Array and, in both cases, it shall be ignored.
289     * 
290     * @see Builder#extraSwitchesJAVAC
291     */
292    public String[] extraSwitchesJAVAC = null;
293
294    /**
295     * For Stage 2, the {@code javadoc} Stage, this Configuration-Field may be assigned any
296     * switch-{@code String's}, and they will be passed to the {@code javadoc} invocation.
297     * 
298     * <BR /><BR />This field's value may be left or null, or assigned a zero-length 
299     * {@code String[]}-Array, and, in both cases, it shall be ignored.
300     * 
301     * @see Builder#extraSwitchesJAVADOC
302     */
303    public String[] extraSwitchesJAVADOC = null;
304
305    /**
306     * The list of Java Packages to compile.  This list may be neither empty nor null, or an
307     * exception will throw.
308     * 
309     * @see Builder#packageList
310     */
311    public BuildPackage[] packageList = null;
312
313    /**
314     * The Root Class-File Directories to use with {@code 'javac'}.  This is used to generate the
315     * value passed to the {@code -cp} switch passed when <B>Stage 1</B> invokes the Java-Compiler.
316     * This parameter / field may be empty or null, and if it is, it will be ignored.
317     */
318    public String[] CLASS_PATH = null;
319
320    /**
321     * This allows for specifying a list of files to manually add into the {@code '.jar'} that is
322     * built by this package.
323     * 
324     * <BR /><BR /><B CLASS=JDDescLabel>Constructor Call:</B>
325     * 
326     * <BR />Because it isn't necessary to include any "Extra Files" into the {@code '.jar'}, there
327     * is some likelihood that this Configuration-Field wouldn't ever be needed / used.  Make sure
328     * to call the {@link JarInclude} constructor, first, to build an instance of this Config-Field
329     * before attempting to use it.  Notice that it is initially initialized to null!
330     * 
331     * <BR /><BR /><B CLASS=JDDescLabel>Common Usess:</B>
332     * 
333     * <BR />Java-HTML has quite a few Data-Files that are inserted into the Jar-File, manually,
334     * using this class.  There may also be the need to insert {@code META-INF/} directory files
335     * that name things like Annotation-Processor locations, taglets, or other Java Services.
336     * 
337     * @see Builder#jarIncludes
338     */
339    public JarInclude jarIncludes = null;
340
341    /**
342     * Requests that when {@code 'javac'} is invoked, the switch {@code -Xlint:all,-processing} is
343     * included in the command-line invocation.
344     * 
345     * @see Builder#USE_XLINT_SWITCH
346     */
347    public boolean USE_XLINT_SWITCH = true;
348
349    /**
350     * Requests that when {@code 'javac'} is invoked, the switch {@code -Xdiags:verbose} is
351     * included in the command-line invocation.
352     * 
353     * @see Builder#USE_XDIAGS_SWITCH
354     */
355    public boolean USE_XDIAGS_SWITCH = true;
356
357    /**
358     * JavaDoc Frames were a true majesty that should never have been eliminated.  If you would
359     * prefer to remove them, or you are using JDK 21+ which no longer accepts the {@code --frames}
360     * switch, then set this Configuration-Field to {@code FALSE}.
361     * 
362     * @see Builder#NO_JAVADOC_FRAMES_SWITCH
363     */
364    public boolean NO_JAVADOC_FRAMES_SWITCH = false;
365
366
367    // ********************************************************************************************
368    // ********************************************************************************************
369    // Configuration Instance Fields
370    // ********************************************************************************************
371    // ********************************************************************************************
372
373
374    /**
375     * Creates an instance of this class, with all default values assigned to the parameter /
376     * fields.
377     * 
378     * <BR /><BR />The intention is that an instance of this class will have its
379     * Configuration-Fields property assigned, according to the needs of the Project being built.
380     * Afterwards, the {@code 'Config'} instance may be passed to the {@code 'Builder'}-Constructor
381     * and then that instance may be used to build the project.
382     * 
383     * @see Builder#Builder(Config, String...)
384     * @see Builder#build()
385     */
386    public Config() { }
387
388
389    // ********************************************************************************************
390    // ********************************************************************************************
391    // Configuration Instance Fields
392    // ********************************************************************************************
393    // ********************************************************************************************
394
395
396    /**
397     * Used by class {@link Builder} to validate all user-provided data on a best efforts basis.
398     * 
399     * @throws NullPointerException If any of the configuration parameter/fields listed here have
400     * been assigned null
401     * 
402     * <BR /><UL CLASS=JDUL>
403     * <LI>{@link #LOCAL_JAVADOC_DIR}</LI>
404     * <LI>{@link #PROJECT_NAME}</LI>
405     * <LI>{@link #JAVAC_BIN}</LI>
406     * <LI>{@link #JAVADOC_BIN}</LI>
407     * <LI>{@link #TAR_SOURCE_DIR}</LI>
408     * <LI>{@link #LOG_DIR}</LI>
409     * <LI>{@link #upgrader}</LI>
410     * <LI>{@link #packageList}</LI>
411     * </UL>
412     * 
413     * <BR />This exception will also be thrown if these lists contain any null-valued
414     * entries:
415     * <BR /><UL CLASS=JDUL>
416     * <LI>{@link #packageList}</LI>
417     * <LI>{@link #CLASS_PATH}</LI>
418     * </UL>
419     * 
420     * @throws IllegalArgumentException If any of these configuration fields were assigned a 
421     * negative number:
422     * <BR /><UL CLASS=JDUL>
423     * <LI>{@link VERSION_MAJOR_NUMBER}</LI>
424     * <LI>{@link VERSION_MINOR_NUMBER}</LI>
425     * <LI>{@link JAVADOC_VER}</LI>
426     * </UL>
427     * 
428     * <BR />This exception will also be thrown if:
429     * <BR /><UL CLASS=JDUL>
430     * <LI> {@link #PROJECT_NAME} does not pass the {@link #projectNameValidator} test.</LI>
431     * </UL>
432     * 
433     * @throws FileNotFoundException This exception will throw if either of these fields are not
434     * assigned a valid File-System Directory-Name:
435     * <BR /><UL CLASS=JDUL>
436     * <LI> {@link #FAVICON}</LI>
437     * <LI> {@link #TAR_SOURCE_DIR}</LI>
438     * </UL>
439     * 
440     * @throws UnrecognizedImageExtException If the {@link #FAVICON} Image-Format cannot be 
441     * properly "guessed" by class {@link IF}.
442     * 
443     * @throws EmptyListException If {@link #packageList} has been assigned an empty list.
444     * 
445     * @throws WriteableDirectoryException If parameter {@link #JAR_FILE_MOVE_DIR} is assigned a 
446     * non-null value, but the {@code String} assigned it does not point to a File-System
447     * directory, or that directory is not writeable.
448     * 
449     * <BR /><BR />Also throws if {@link #LOG_DIR} is not a valid directory-name, that allows
450     * write-access.
451     */
452    public final void validate() throws java.io.FileNotFoundException
453    {
454        Objects.requireNonNull(LOCAL_JAVADOC_DIR,   M1 + "LOCAL_JAVADOC_DIR" + M2);
455        Objects.requireNonNull(PROJECT_NAME,        M1 + "PROJECT_NAME" + M2);
456        Objects.requireNonNull(JAVAC_BIN,           M1 + "JAVAC_BIN" + M2);
457        Objects.requireNonNull(JAVADOC_BIN,         M1 + "JAVADOC_BIN" + M2);
458        Objects.requireNonNull(TAR_SOURCE_DIR,      M1 + "TAR_SOURCE_DIR" + M2);
459        Objects.requireNonNull(LOG_DIR,             M1 + "LOG_DIR" + M2);
460        Objects.requireNonNull(upgrader,            M1 + "upgrader" + M2);
461        Objects.requireNonNull(packageList,         M1 + "packageList" + M2);
462
463        if (! projectNameValidator.test(PROJECT_NAME)) throw new IllegalArgumentException
464            (M1 + "PROJECT_NAME] was not passed a valid name.");
465
466        if (VERSION_MAJOR_NUMBER < 0) throw new IllegalArgumentException
467            (M1 + "VERSION_MAJOR_NUMBER" + M3 + VERSION_MAJOR_NUMBER);
468
469        if (VERSION_MINOR_NUMBER < 0) throw new IllegalArgumentException
470            (M1 + "VERSION_MINOR_NUMBER" + M3 + VERSION_MINOR_NUMBER);
471
472        if (JAVADOC_VER < 0) throw new IllegalArgumentException
473            (M1 + "JAVADOC_VER" + M3 + JAVADOC_VER);
474
475        if (FAVICON != null)
476        {
477            File f = new File(FAVICON);
478
479            if ((! f.exists()) || (! f.isFile())) throw new FileNotFoundException(
480                "Field 'FAVICON' [" + FAVICON + "] points to a file that was not found, or " +
481                "isn't a File"
482            );
483
484            IF.guessOrThrow(FAVICON);
485        }
486
487        if (packageList.length == 0) throw new EmptyListException
488            (M1 + "packageList] was passed a list of size 0.");
489
490        for (BuildPackage bp : packageList) if (bp == null) throw new NullPointerException
491            (M1 + "packageList] contains a null value.");
492
493        for (String p : CLASS_PATH) if (p == null) throw new NullPointerException
494            (M1 + "CLASS_PATH] contains a null value.");
495
496        File f = new File(TAR_SOURCE_DIR);
497        if (! (f.exists() && f.isDirectory())) throw new FileNotFoundException
498            (M1 + "] was not passed a vaild File-System Directory-Name.");
499
500        if (JAR_FILE_MOVE_DIR != null)
501        {
502            WritableDirectoryException.check(JAR_FILE_MOVE_DIR);
503
504            if (! JAR_FILE_MOVE_DIR.endsWith(File.separator))
505                JAR_FILE_MOVE_DIR = JAR_FILE_MOVE_DIR + File.separator;
506        }
507
508        WritableDirectoryException.check(LOG_DIR);
509
510        if (! LOG_DIR.endsWith(File.separator)) LOG_DIR = LOG_DIR + File.separator;
511    }
512
513    private static final String M1 = "The Parameter-Field Configuration [";
514    private static final String M2 = "] was ultimately assigned null.";
515    private static final String M3 = "] was passed a negative number: ";
516
517    final String classPathStr(boolean INCLUDE_JAR_IN_CP, String JAR_FILE_NAME)
518    {
519        if (CLASS_PATH == null) return INCLUDE_JAR_IN_CP ? JAR_FILE_NAME : null;
520
521        final String sep = StrCmpr.containsIgnoreCase(System.getProperty("os.name"), "win")
522            ? ";"
523            : ":";
524
525        final StringBuilder sb  = new StringBuilder();
526
527        for (int i=0; i < CLASS_PATH.length; i++) sb.append(((i==0) ? "" : sep) + CLASS_PATH[i]);
528
529        if (INCLUDE_JAR_IN_CP) sb.append(sep + JAR_FILE_NAME);
530
531        return sb.toString();
532    }
533}