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" }; } |