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

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

import Torello.Java.Additional.ByteArrClassLoader;

import Torello.Java.ReadOnly.ReadOnlyList;
import Torello.Java.ReadOnly.ReadOnlyArrayList;

import static Torello.Java.C.BYELLOW;
import static Torello.Java.C.BGREEN;
import static Torello.Java.C.BRED;
import static Torello.Java.C.BWHITE;
import static Torello.Java.C.BRED_BKGND;
import static Torello.Java.C.RESET;

import java.io.IOException;
import java.io.File;

// All this thing does is enter the "bp" directory that is passed to this class
// only method.  That directory is, suprisingly, "bp.pkgRootDirector" + "data-files/".
// 
// It scans the *ENTIRE* directory-tree (which, literally, just means that directory and also any 
// and all sub-directories it has) for all Java '.class'-Files.
// 
// Each '.class' File that is found is checked to see whether it is Assignable-To the interface 
// "Torello.Java.Build.DataFileBuilderClass.class".  If a '.class'-File is assignable into that 
// interface, then it must have a 'build(String rootDir)' method.  Any '.class' File which does 
// implement that interface (and therefore has the 'build' method just mention) is invoked!
// 
// IT IS UP TO THE USER TO MAKE SURE THAT THE SPECIFIED 'build(String)' METHOD ACTUALLY OUTPUTS
// ANY-AND-ALL DATA-FILES EXPECTED FOR THAT PACKAGE, INTO ITS "data-files/" SUB-DIRECTORY.

class RunDataFilesBuilders
{
    @SuppressWarnings({"unchecked", "rawtypes"})
    static int run(final BuildPackage bp) throws IOException
    {
        final ByteArrClassLoader    bacl    = new ByteArrClassLoader();
        final String                rootDir = bp.pkgRootDirectory + "data-files" + File.separator;


        // Read All '.class' Files, and then generate a List<Class> of all of the Class-Files which
        // implement the DataFileBuilderClass interface.  Save those classes into a ReadOnlyList

        @SuppressWarnings("rawtypes")
        final ReadOnlyList<Class> dfbClasses = FileNode
            .createRoot(rootDir)
            .loadTree(-1, FileNode.CLASS_FILES, null)
            .flattenJustFiles(RTC.FULLPATH_STREAM())
            .map((String fileName) -> (Class) bacl.readJavaClassNOIOE(fileName))
            .filter((Class c) -> DataFileBuilderClass.class.isAssignableFrom(c))
            .collect(ReadOnlyArrayList.streamCollector());

        // If there are no such classes, print a message and return immediately
        if (dfbClasses.size() == 0)
        {
            System.out.println(
                '\n' +
                "Data-Files Directory:\n    " + BYELLOW + rootDir + RESET + '\n' +
                "Does not contain any '.class' Files which implement the DataFileBuilderClass " +
                    "interface\n\n" +
                "Exiting..."
            );

            return 0;
        }

        // Iterate all classes which implemented the DataFileBuilderClass interface!
        for (final Class c : dfbClasses)
        {
            final String cName = c.getCanonicalName();

            System.out.println(
                '\n' +
                BRED_BKGND + BWHITE + " Running Class: " + RESET + ' ' +
                BGREEN + cName + RESET + '\n'
            );

            final DataFileBuilderClass dfbc;

            try
                { dfbc = (DataFileBuilderClass) c.getDeclaredConstructor().newInstance(); }

            // NOTE: Catching Throwable-Class "Error" is extremely uncommon in Java, but this
            //       really is one of the places where you have to do it.f
            //  
            // The Method "newInstance" throws an "ExceptionInInitializerError" if the user's
            // Class throws an exception when attempting to build a new instance using the class'
            // Zero-Argument Constructor.
            // 
            // Not Exactly sure why ExceptionInInitializerError is an Error, rather than Exception,
            //  but I'm really not going to argue about it right now...

            catch (Exception | Error e)
            {
                System.out.println(
                    EXCC.toString(e) + '\n' +
                    e.getMessage() + "\n\n" +
                    "For DataFileBuildClass:\n    " + BYELLOW + cName + RESET + '\n' +
                    "Unable to invoke the Zero-Argument Constructor for that Class.\n" +
                    "Did you remember to declare a public, Zero-Argument Constructor?\n"
                );

                System.exit(0); /* shhh compiler !! */ throw null;
            }

            dfbc.build(rootDir);
        }

        System.out.println();


        // This is the count on how many Class-Files 'build(String)' methods were invoked.
        // It is placed into a Counts-Table, so that a Count-Summary may be printed at the end
        // of the "BuildDataFiles" experience!

        return dfbClasses.size();
    }
}