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
package Torello.Java.Build;

import java.io.File;
import java.io.IOException;
import java.util.List;

import Torello.Java.StringParse;
import Torello.Java.FileNode;
import Torello.Java.RTC;
import Torello.Java.EXCC;
import Torello.Java.FileRW;

/**
 * Can be used by Classes the need to build Data-File(s) for the {@code 'data-files/'}
 * directory, or convert those Data-Files to Text for viewing and inspection.
 */
public abstract class DFBuilder
{
    // ********************************************************************************************
    // ********************************************************************************************
    // Instance Abstract Methods
    // ********************************************************************************************
    // ********************************************************************************************


    /** Requests a list of Data-File Names that are constructed by this instance. */
    public abstract Iterable<String> dataFileNames();

    /**
     * Requests that this Data-File Builder run and generate all Data-Files that it can build.
     * 
     * @param dataFileRootDir The target directory location for writing the file(s)
     * 
     * <BR /><BR /><B STYLE='color: red;'>Relative Directory Path:</B> This parameter is necessary
     * becase an instance of {@code DFBuilder} might be invoked from just about anywhere.  Thus, it
     * is imperative that the actual root {@code '../data-files/'} directory be passed to this 
     * method, to avoid writing the Data-Files into the wrong place.
     * 
     * <BR /><BR /><I>This {@code String}-Parameter {@code 'dataFileRootDir'} must be a
     * {@code String} that is a Relative-Path, from the CWD / Current-Working-Directory from whence
     * the current Java-Instance was invoked,  to the {@code '../data-files/'} directory where the
     * Data-Files are to be written</I>.
     * 
     * @throws IOException If there are any problems while writing the data-file.
     */
    public abstract void buildAll(String dataFileRootDir) throws IOException;

    /**
     * Requests that this Data-File Builder generate all Data-Files as Text-Files, so that
     * they may be viewed and inspected.
     * 
     * <EMBED CLASS=external-html DATA-FILE-ID=DEF_IMPL_UOEX>
     * 
     * @param targetDir <EMBED CLASS=external-html DATA-FILE-ID=DFB_TARGET_DIR>
     * @throws IOException <EMBED CLASS=external-html DATA-FILE-ID=IOEX>
     * @throws UnsupportedOperationException    <EMBED CLASS=external-html DATA-FILE-ID=UOEX>
     *
     */
    public void buildAllToText(String targetDir) throws IOException
    { throw new UnsupportedOperationException(); }

    /**
     * Requests that this Data-File Builder convert the existing Data-Files themselves into 
     * Text-Files, so that they may be viewed and inspected.
     * 
     * <EMBED CLASS=external-html DATA-FILE-ID=DEF_IMPL_UOEX>
     * 
     * @param targetDir <EMBED CLASS=external-html DATA-FILE-ID=DFB_TARGET_DIR>
     * @throws IOException <EMBED CLASS=external-html DATA-FILE-ID=IOEX>
     * @throws UnsupportedOperationException    <EMBED CLASS=external-html DATA-FILE-ID=UOEX>
     */

    public void deCompileAllToText(String targetDir) throws IOException
    { throw new UnsupportedOperationException(); }


    // ********************************************************************************************
    // ********************************************************************************************
    // Utility for building all Data-Files in all Packages that have a "../data-files/" directory
    // ********************************************************************************************
    // ********************************************************************************************


    /**
     * Runs / Executes the method {@link buildAll(String)} on all Class-File(s) found insdie the
     * {@code 'data-files/'} directory for a given package.  If the package provided to parameter
     * {@code 'pkg'} does not have a {@code 'data-files/'} directory, then this method exists 
     * gracefully
     * 
     * @param pkg Any one of the Configured Project Packages.  The relevant fields used by this 
     * method are {@link BuildPackage#classPathLocation} and {@link BuildPackage#pkgRootDirectory}.
     * These two fields are concatenated, and that directory is searched for any / all class files
     * that implement this {@code DFBuilder} interface.
     * 
     * @return The number of class-files implementing the {@code DFBuilder} interface.
     */
    @SuppressWarnings("unchecked")
    public static int buildAll(final BuildPackage pkg) throws IOException
    {
        String  dirName = pkg.classPathLocation + pkg.pkgRootDirectory;
        File    f       = new File(dirName);

        if ((! f.exists()) || (! f.isDirectory())) return 0;

        String[] fNameArr = FileNode
            .createRoot(dirName)
            .loadTree(-1, (File dir, String fName) -> fName.endsWith(".class"), null)
            .flattenJustFiles(RTC.FULLPATH_ARRAY());

        int counter = 0;

        TOP:
        for (String classFileName : fNameArr)
        {
            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
            // FileRW.readClass
            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

            // Pulls the Class-Name out of the Class-FileName:
            //      1) Removes the ".class" extension
            //      2) Removes the leading "data-files/..." directory string stuff from File-Name

            final String className = StringParse.beforeExtension
                (StringParse.fromLastFileSeparatorPos(classFileName));

            // Runs the FileRW.readClass method & catches the exceptions it throws.
            final Class<?> c;

            try
                { c = FileRW.readClass(classFileName, className); }

            catch (Exception e)
                { continue; }


            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
            // Make sure class is a DFBuilder instance
            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

            Class<?> temp = c;

            while (true)

                if (temp == null)                   continue TOP;
                else if (temp == Object.class)      continue TOP;
                else if (temp == DFBuilder.class)   break;
                else                                temp = temp.getSuperclass();


            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
            // Instantiate and call instance buildAll()
            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

            final DFBuilder dfb;

            try
                { dfb = ((Class<DFBuilder>) c).getDeclaredConstructor().newInstance(); }

            catch (Exception e)
            {
                System.out.println(
                    "Unable to instantiate DFBuilder.\n" +
                    "Do you have a Zero-Argument Public Constructor?\n" +
                    EXCC.toString(e)
                );

                System.exit(1);
                return -1; // Shut-Up Compiler
            }
            
            counter++;
            dfb.buildAll(dirName);
        }

        return counter;
    }


    // ********************************************************************************************
    // ********************************************************************************************
    // Command-Line Interface Helper Method (public-static-void-main, to be used by sub-classes)
    // ********************************************************************************************
    // ********************************************************************************************


    /**
     * This method is intended to be used by classes that inherit this abstract class.  Classes
     * that inherit {@code DFBuilder} may add a method into their implementation, as in the hilited
     * source-code below.  This provides a standard Java {@code 'Main'}-Method that may be invoked
     * as a <B STYLE='color: red;'>CLI: Command Line Interface</B>.
     * 
     * <DIV CLASS=EXAMPLE>{@code
     * // The class extending DFBuilder can use this method so that it can be invoked at the
     * // command line.  Passing '1' means that only the 'buildAll' was written by the extending
     * // Type.
     * 
     * public static void main(String[] argv) { new YourDataFileBuilder().cli(1, argv); }
     * }</DIV>
     * 
     * @param menuOptions The value passed to this parameter must be one of the values excplicitly
     * named in this set: <B>{@code 1, 2, 3, 4, 6}</B>.  Any value other than these 5 numbers will
     * force this method to throw an {@code IllegalArgumentException}.
     * 
     * <BR /><BR />This meaning of these is clearly explained, in the table below:
     * 
     * <BR /><BR /><TABLE CLASS=JDBriefTable>
     * <TR><TH>{@code 'menuOptions'}</TH><TH>Meaning</TH></TR>
     * 
     * <TR> <TD>'1'</TD>
     *      <TD>The only method implemented is the {@link #buildAll(String) buildAll} method.  The
     *          others, on invokation, will throw the {@code UnsupportedOperationException}
     *          </TD></TR>
     * 
     * <TR> <TD>'3'</TD>
     *      <TD>{@code 3 => 1 & 2}<BR />
     *          Methods {@link #buildAll(String) buildAll} and {@link #buildAllToText(String)
     *          buildAllToText} are implemented, and may be invoked.
     *
     *          <BR /><BR >Attempting to call {@link #deCompileAllToText(String)
     *          deCompileAllToText} would cause an {@code UnsupportedOperationException} to throw.
     *          </TD></TR>
     * 
     * <TR> <TD>'4'</TD>
     *      <TD>{@code 4 => 1 & 3}<BR />
     *          Methods {@link #buildAll(String) buildAll} and {@link #deCompileAllToText(String)
     *          deCompileAllToText} are implemented, and may be invoked.
     *
     *          <BR /><BR >Attempting to call {@link #buildAllToText(String) buildAllToText} would 
     *          cause an {@code UnsupportedOperationException} to throw.
     *          </TD></TR>
     *
     * <TR> <TD>'6'</TD>
     *      <TD>{@code 6 ==> 1, 2 & 3}<BR />
     *          All three methods exported by this class have been implemented, and may be invoked
     *          without worry that the {@code UnsupportedOperationException} will throw.
     *          </TD></TR>
     * 
     * </TABLE>
     * 
     * @param argv The Command-Line Parameter {@code String[]}-Array.  This should just be the 
     * {@code argv}-Array that was sent to the actual Java {@code 'Main'}-Method that his invoking
     * this helper.
     * 
     * @return {@code TRUE} if and only if the invoked builder method terminated successfully.
     */
    public final boolean cli(int menuOptions, String[] argv)
    {
        if ((menuOptions < 1) || (menuOptions > 6) || (menuOptions == 2) || (menuOptions == 5))

            throw new IllegalArgumentException(
                "Parameter 'menuOptions' must be a value in this set: [1, 3, 4, 6]\n" + 
                "These are the only valid values to pass here, but [" + menuOptions + "] was " +
                    "provided, instead."
            );

        if ((argv.length != 1) && (argv.length != 2))
        {
            printManPage(menuOptions);
            return false;
        }

        final boolean   twoOK   = (menuOptions == 3) || (menuOptions == 6);
        final boolean   threeOK = (menuOptions == 4) || (menuOptions == 6);
        final String    dirName = (argv.length == 2) ? argv[1] : "";

        try
        {
            switch (argv[0])
            {
                case "1":
                    buildAll(dirName);
                    break;

                case "2":
                    if (twoOK)  buildAllToText(dirName);
                    else        printManPage(menuOptions);
                    break;

                case "3":
                    if (threeOK)    deCompileAllToText(dirName);
                    else            printManPage(menuOptions);
                    break;
            }
        }

        catch (Exception e)
        {
            System.out.println(
                "This DFBuilder Instance has thrown an Exception while writing a File:\n" +
                EXCC.toString(e) + '\n'
            );

            return false;
        }

        return true;
    }

    private static void printManPage(int menuChoice)
    {
        System.out.println(MAN_PAGE_LINES[0]);

        if ((menuChoice == 3) || (menuChoice == 6))
            System.out.println(MAN_PAGE_LINES[1]);

        if ((menuChoice == 4) || (menuChoice == 6))
            System.out.println(MAN_PAGE_LINES[2]);

        System.out.println("\n\tOptional Directory-Name may also be passed\n");
    }

    private static final String[] MAN_PAGE_LINES = 
    {
        "Command Line Argument:\n\n" +
        "\t1: Build All Data-Files Produced by this class",

        "\t2: Generate Data-Files as Text-Files, for review and inspection",

        "\t3: Dump Data-File(s) Produced by this Class to Text-Filess"
    };
}