001package Torello.Java.Build; 002 003import java.io.File; 004import java.io.IOException; 005import java.util.List; 006 007import Torello.Java.StringParse; 008import Torello.Java.FileNode; 009import Torello.Java.RTC; 010import Torello.Java.EXCC; 011import Torello.Java.FileRW; 012 013/** 014 * Can be used by Classes the need to build Data-File(s) for the {@code 'data-files/'} 015 * directory, or convert those Data-Files to Text for viewing and inspection. 016 */ 017public abstract class DFBuilder 018{ 019 // ******************************************************************************************** 020 // ******************************************************************************************** 021 // Instance Abstract Methods 022 // ******************************************************************************************** 023 // ******************************************************************************************** 024 025 026 /** Requests a list of Data-File Names that are constructed by this instance. */ 027 public abstract Iterable<String> dataFileNames(); 028 029 /** 030 * Requests that this Data-File Builder run and generate all Data-Files that it can build. 031 * 032 * @param dataFileRootDir The target directory location for writing the file(s) 033 * 034 * <BR /><BR /><B STYLE='color: red;'>Relative Directory Path:</B> This parameter is necessary 035 * becase an instance of {@code DFBuilder} might be invoked from just about anywhere. Thus, it 036 * is imperative that the actual root {@code '../data-files/'} directory be passed to this 037 * method, to avoid writing the Data-Files into the wrong place. 038 * 039 * <BR /><BR /><I>This {@code String}-Parameter {@code 'dataFileRootDir'} must be a 040 * {@code String} that is a Relative-Path, from the CWD / Current-Working-Directory from whence 041 * the current Java-Instance was invoked, to the {@code '../data-files/'} directory where the 042 * Data-Files are to be written</I>. 043 * 044 * @throws IOException If there are any problems while writing the data-file. 045 */ 046 public abstract void buildAll(String dataFileRootDir) throws IOException; 047 048 /** 049 * Requests that this Data-File Builder generate all Data-Files as Text-Files, so that 050 * they may be viewed and inspected. 051 * 052 * <EMBED CLASS=external-html DATA-FILE-ID=DEF_IMPL_UOEX> 053 * 054 * @param targetDir <EMBED CLASS=external-html DATA-FILE-ID=DFB_TARGET_DIR> 055 * @throws IOException <EMBED CLASS=external-html DATA-FILE-ID=IOEX> 056 * @throws UnsupportedOperationException <EMBED CLASS=external-html DATA-FILE-ID=UOEX> 057 * 058 */ 059 public void buildAllToText(String targetDir) throws IOException 060 { throw new UnsupportedOperationException(); } 061 062 /** 063 * Requests that this Data-File Builder convert the existing Data-Files themselves into 064 * Text-Files, so that they may be viewed and inspected. 065 * 066 * <EMBED CLASS=external-html DATA-FILE-ID=DEF_IMPL_UOEX> 067 * 068 * @param targetDir <EMBED CLASS=external-html DATA-FILE-ID=DFB_TARGET_DIR> 069 * @throws IOException <EMBED CLASS=external-html DATA-FILE-ID=IOEX> 070 * @throws UnsupportedOperationException <EMBED CLASS=external-html DATA-FILE-ID=UOEX> 071 */ 072 073 public void deCompileAllToText(String targetDir) throws IOException 074 { throw new UnsupportedOperationException(); } 075 076 077 // ******************************************************************************************** 078 // ******************************************************************************************** 079 // Utility for building all Data-Files in all Packages that have a "../data-files/" directory 080 // ******************************************************************************************** 081 // ******************************************************************************************** 082 083 084 /** 085 * Runs / Executes the method {@link buildAll(String)} on all Class-File(s) found insdie the 086 * {@code 'data-files/'} directory for a given package. If the package provided to parameter 087 * {@code 'pkg'} does not have a {@code 'data-files/'} directory, then this method exists 088 * gracefully 089 * 090 * @param pkg Any one of the Configured Project Packages. The relevant fields used by this 091 * method are {@link BuildPackage#classPathLocation} and {@link BuildPackage#pkgRootDirectory}. 092 * These two fields are concatenated, and that directory is searched for any / all class files 093 * that implement this {@code DFBuilder} interface. 094 * 095 * @return The number of class-files implementing the {@code DFBuilder} interface. 096 */ 097 @SuppressWarnings("unchecked") 098 public static int buildAll(final BuildPackage pkg) throws IOException 099 { 100 String dirName = pkg.classPathLocation + pkg.pkgRootDirectory; 101 File f = new File(dirName); 102 103 if ((! f.exists()) || (! f.isDirectory())) return 0; 104 105 String[] fNameArr = FileNode 106 .createRoot(dirName) 107 .loadTree(-1, (File dir, String fName) -> fName.endsWith(".class"), null) 108 .flattenJustFiles(RTC.FULLPATH_ARRAY()); 109 110 int counter = 0; 111 112 TOP: 113 for (String classFileName : fNameArr) 114 { 115 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 116 // FileRW.readClass 117 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 118 119 // Pulls the Class-Name out of the Class-FileName: 120 // 1) Removes the ".class" extension 121 // 2) Removes the leading "data-files/..." directory string stuff from File-Name 122 123 final String className = StringParse.beforeExtension 124 (StringParse.fromLastFileSeparatorPos(classFileName)); 125 126 // Runs the FileRW.readClass method & catches the exceptions it throws. 127 final Class<?> c; 128 129 try 130 { c = FileRW.readClass(classFileName, className); } 131 132 catch (Exception e) 133 { continue; } 134 135 136 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 137 // Make sure class is a DFBuilder instance 138 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 139 140 Class<?> temp = c; 141 142 while (true) 143 144 if (temp == null) continue TOP; 145 else if (temp == Object.class) continue TOP; 146 else if (temp == DFBuilder.class) break; 147 else temp = temp.getSuperclass(); 148 149 150 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 151 // Instantiate and call instance buildAll() 152 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 153 154 final DFBuilder dfb; 155 156 try 157 { dfb = ((Class<DFBuilder>) c).getDeclaredConstructor().newInstance(); } 158 159 catch (Exception e) 160 { 161 System.out.println( 162 "Unable to instantiate DFBuilder.\n" + 163 "Do you have a Zero-Argument Public Constructor?\n" + 164 EXCC.toString(e) 165 ); 166 167 System.exit(1); 168 return -1; // Shut-Up Compiler 169 } 170 171 counter++; 172 dfb.buildAll(dirName); 173 } 174 175 return counter; 176 } 177 178 179 // ******************************************************************************************** 180 // ******************************************************************************************** 181 // Command-Line Interface Helper Method (public-static-void-main, to be used by sub-classes) 182 // ******************************************************************************************** 183 // ******************************************************************************************** 184 185 186 /** 187 * This method is intended to be used by classes that inherit this abstract class. Classes 188 * that inherit {@code DFBuilder} may add a method into their implementation, as in the hilited 189 * source-code below. This provides a standard Java {@code 'Main'}-Method that may be invoked 190 * as a <B STYLE='color: red;'>CLI: Command Line Interface</B>. 191 * 192 * <DIV CLASS=EXAMPLE>{@code 193 * // The class extending DFBuilder can use this method so that it can be invoked at the 194 * // command line. Passing '1' means that only the 'buildAll' was written by the extending 195 * // Type. 196 * 197 * public static void main(String[] argv) { new YourDataFileBuilder().cli(1, argv); } 198 * }</DIV> 199 * 200 * @param menuOptions The value passed to this parameter must be one of the values excplicitly 201 * named in this set: <B>{@code 1, 2, 3, 4, 6}</B>. Any value other than these 5 numbers will 202 * force this method to throw an {@code IllegalArgumentException}. 203 * 204 * <BR /><BR />This meaning of these is clearly explained, in the table below: 205 * 206 * <BR /><BR /><TABLE CLASS=JDBriefTable> 207 * <TR><TH>{@code 'menuOptions'}</TH><TH>Meaning</TH></TR> 208 * 209 * <TR> <TD>'1'</TD> 210 * <TD>The only method implemented is the {@link #buildAll(String) buildAll} method. The 211 * others, on invokation, will throw the {@code UnsupportedOperationException} 212 * </TD></TR> 213 * 214 * <TR> <TD>'3'</TD> 215 * <TD>{@code 3 => 1 & 2}<BR /> 216 * Methods {@link #buildAll(String) buildAll} and {@link #buildAllToText(String) 217 * buildAllToText} are implemented, and may be invoked. 218 * 219 * <BR /><BR >Attempting to call {@link #deCompileAllToText(String) 220 * deCompileAllToText} would cause an {@code UnsupportedOperationException} to throw. 221 * </TD></TR> 222 * 223 * <TR> <TD>'4'</TD> 224 * <TD>{@code 4 => 1 & 3}<BR /> 225 * Methods {@link #buildAll(String) buildAll} and {@link #deCompileAllToText(String) 226 * deCompileAllToText} are implemented, and may be invoked. 227 * 228 * <BR /><BR >Attempting to call {@link #buildAllToText(String) buildAllToText} would 229 * cause an {@code UnsupportedOperationException} to throw. 230 * </TD></TR> 231 * 232 * <TR> <TD>'6'</TD> 233 * <TD>{@code 6 ==> 1, 2 & 3}<BR /> 234 * All three methods exported by this class have been implemented, and may be invoked 235 * without worry that the {@code UnsupportedOperationException} will throw. 236 * </TD></TR> 237 * 238 * </TABLE> 239 * 240 * @param argv The Command-Line Parameter {@code String[]}-Array. This should just be the 241 * {@code argv}-Array that was sent to the actual Java {@code 'Main'}-Method that his invoking 242 * this helper. 243 * 244 * @return {@code TRUE} if and only if the invoked builder method terminated successfully. 245 */ 246 public final boolean cli(int menuOptions, String[] argv) 247 { 248 if ((menuOptions < 1) || (menuOptions > 6) || (menuOptions == 2) || (menuOptions == 5)) 249 250 throw new IllegalArgumentException( 251 "Parameter 'menuOptions' must be a value in this set: [1, 3, 4, 6]\n" + 252 "These are the only valid values to pass here, but [" + menuOptions + "] was " + 253 "provided, instead." 254 ); 255 256 if ((argv.length != 1) && (argv.length != 2)) 257 { 258 printManPage(menuOptions); 259 return false; 260 } 261 262 final boolean twoOK = (menuOptions == 3) || (menuOptions == 6); 263 final boolean threeOK = (menuOptions == 4) || (menuOptions == 6); 264 final String dirName = (argv.length == 2) ? argv[1] : ""; 265 266 try 267 { 268 switch (argv[0]) 269 { 270 case "1": 271 buildAll(dirName); 272 break; 273 274 case "2": 275 if (twoOK) buildAllToText(dirName); 276 else printManPage(menuOptions); 277 break; 278 279 case "3": 280 if (threeOK) deCompileAllToText(dirName); 281 else printManPage(menuOptions); 282 break; 283 } 284 } 285 286 catch (Exception e) 287 { 288 System.out.println( 289 "This DFBuilder Instance has thrown an Exception while writing a File:\n" + 290 EXCC.toString(e) + '\n' 291 ); 292 293 return false; 294 } 295 296 return true; 297 } 298 299 private static void printManPage(int menuChoice) 300 { 301 System.out.println(MAN_PAGE_LINES[0]); 302 303 if ((menuChoice == 3) || (menuChoice == 6)) 304 System.out.println(MAN_PAGE_LINES[1]); 305 306 if ((menuChoice == 4) || (menuChoice == 6)) 307 System.out.println(MAN_PAGE_LINES[2]); 308 309 System.out.println("\n\tOptional Directory-Name may also be passed\n"); 310 } 311 312 private static final String[] MAN_PAGE_LINES = 313 { 314 "Command Line Argument:\n\n" + 315 "\t1: Build All Data-Files Produced by this class", 316 317 "\t2: Generate Data-Files as Text-Files, for review and inspection", 318 319 "\t3: Dump Data-File(s) Produced by this Class to Text-Filess" 320 }; 321}