001package Torello.Java; 002 003import java.io.*; 004import java.util.*; 005import java.util.zip.*; 006import java.util.stream.*; 007import java.nio.file.*; 008import java.lang.reflect.InvocationTargetException; 009 010import Torello.Java.Additional.*; 011 012import Torello.JavaDoc.Annotations.StaticFunctional; 013import Torello.JavaDoc.Annotations.StaticFunctional.Excuse; 014 015/** 016 * Operating-System independent File Read & Write utilities that reduce many common Java 017 * File I/O Commands to a single method invocation, focusing heavily on Java's Serialization 018 * feature. 019 * 020 * <EMBED CLASS='external-html' DATA-FILE-ID=FILE_RW> 021 */ 022@StaticFunctional(Excused="TRUNCATE_EOF_CHARS", Excuses=Excuse.FLAG) 023public class FileRW 024{ 025 private FileRW() { } 026 027 /** 028 * This is used by method {@link #loadFileToString(String)}. By default this flag is set to 029 * {@code TRUE}, and when it is, any trailing {@code EOF chars, ASCII-0} found in a file that 030 * is to be interpreted as a Text-File, will be truncated from the {@code String} returned by 031 * that {@code 'reader'} method. 032 */ 033 public static boolean TRUNCATE_EOF_CHARS = true; 034 035 036 // ******************************************************************************************** 037 // ******************************************************************************************** 038 // writeFile 039 // ******************************************************************************************** 040 // ******************************************************************************************** 041 042 043 /** 044 * Writes the entire contents of a single {@code java.lang.String} to a file on the File-System 045 * named {@code 'fName'}. 046 * 047 * <EMBED CLASS='external-html' DATA-FILE-ID=FRW_WRITABLE_DIR> 048 * 049 * @param s A {@code java.lang.String} which is appended, in entirety, to the file ('fName') 050 * 051 * @param fName The name of the file to which the contents of the {@code java.lang.String} 052 * are appended. If This file doesn't already exist, it is created here. 053 * 054 * @throws IOException If any I/O errors have occurred with the File-System / disk. 055 */ 056 public static void writeFile(CharSequence s, String fName) throws IOException 057 { 058 File outF = new File(fName); 059 060 outF.createNewFile(); 061 062 // This writer is 'java.lang.AutoCloseable'. 063 // 064 // NOTE: The IOException will still be thrown out of this method if it occurs. It is not 065 // caught! 066 067 try 068 (FileWriter fw = new FileWriter(outF)) 069 { fw.write(s.toString()); } 070 } 071 072 /** 073 * This takes an {@code Iterable<String>}, and a filename, and writes each 074 * {@code java.lang.String} in the {@code Iterator} that it produces to the file 075 * specified by File-Name parameter {@code 'fName'} 076 * 077 * <EMBED CLASS='external-html' DATA-FILE-ID=FRW_WRITABLE_DIR> 078 * 079 * <BR /><DIV CLASS=JDHintAlt> 080 * <B STYLE='color:red;'>New-Line Characters:</B> A newline {@code ('\n')} is appended to the 081 * end of each {@code java.lang.String} before writing it to the file. 082 * </DIV> 083 * 084 * @param i This is any java {@code Iterable<String>} object. Each of these will be written, 085 * in succession, to the file named by parameter {@code 'fName'} 086 * 087 * @param fName The name of the file to which the contents of the {@code java.lang.String} 088 * are appended. If This file doesn't already exist, it is created here. 089 * 090 * @throws IOException If an I/O error has occurred as a result of the File-System or 091 * disk operation. 092 */ 093 public static void writeFile(Iterable<String> i, String fName) throws IOException 094 { 095 Iterator<String> iter = i.iterator(); 096 097 File outF = new File(fName); 098 099 outF.createNewFile(); 100 101 // This writer is 'java.lang.AutoCloseable'. 102 // 103 // NOTE: The IOException will still be thrown out of this method if it occurs. It is not 104 // caught! 105 106 try 107 (FileWriter fw = new FileWriter(outF)) 108 { while (iter.hasNext()) fw.write(iter.next() + "\n"); } 109 } 110 111 112 // ******************************************************************************************** 113 // ******************************************************************************************** 114 // writeFile_NO_NEWLINE 115 // ******************************************************************************************** 116 // ******************************************************************************************** 117 118 119 /** 120 * This takes an {@code Iterable} of String, and a filename and writes each 121 * {@code java.lang.String} in the {@code Iterator} that it produces to the file specified by 122 * File-Name parameter {@code 'fName'} 123 * 124 * <EMBED CLASS='external-html' DATA-FILE-ID=FRW_WRITABLE_DIR> 125 * 126 * <BR /><DIV CLASS=JDHintAlt> 127 * <B STYLE='color:red;'>New-Line Characters:</B> In this function a newline {@code ('\n')} 128 * character <I>is <B>not</B> appended</I> to the end of each {@code java.lang.String} of the 129 * input {@code Iterator}. 130 * </DIV> 131 * 132 * @param i This is any java {@code 'Iterable'} object that can iterate {@code 'String'}. 133 * Each of these will be written, in succession, to the file named by {@code 'fName'} 134 * 135 * @param fName The name of the file to which the contents of the {@code java.lang.String} 136 * are appended. If This file doesn't already exist, it is created here. 137 * 138 * @throws IOException If an I/O error has occurred as a result of the File-System or disk 139 * operation. 140 */ 141 public static void writeFile_NO_NEWLINE(Iterable<String> i, String fName) throws IOException 142 { 143 Iterator<String> iter = i.iterator(); 144 145 File outF = new File(fName); 146 147 outF.createNewFile(); 148 149 // This Writer is 'java.lang.AutoCloseable'. 150 // 151 // NOTE: The IOException will still be thrown out of this method if it occurs. It is not 152 // caught! 153 154 try 155 (FileWriter fw = new FileWriter(outF)) 156 { while (iter.hasNext()) fw.write(iter.next()); } 157 } 158 159 160 // ******************************************************************************************** 161 // ******************************************************************************************** 162 // appendToFile 163 // ******************************************************************************************** 164 // ******************************************************************************************** 165 166 167 /** 168 * Appends the entire input {@code java.lang.String} - actually a 169 * {@code java.lang.CharSequence} to the file on the File-System named {@code 'fName'} 170 * 171 * <BR /><BR /><DIV CLASS=JDHint> 172 * <B STYLE='color:red;'>Directory Requirements:</B> Though the file does not need to exist 173 * already in order for this file to be written the directory hierarchy needs to exist, or a 174 * Java {@code 'IOException'} will occur. 175 * </DIV> 176 * 177 * @param s A {@code java.lang.CharSequence} (almost identical to {@code 'String'}) which is 178 * appended, in entirety, to the File-Name parameter {@code 'fName'} 179 * 180 * @param fName The name of the file to which the contents of the {@code java.lang.String} are 181 * appended. If This file doesn't already exist, it is created here. 182 * 183 * @throws IOException If an I/O error has occurred as a result of the File-System or 184 * disk operation. 185 */ 186 public static void appendToFile(CharSequence s, String fName) throws IOException 187 { 188 File f = new File(fName); 189 190 if (! f.exists()) f.createNewFile(); 191 192 Files.write(Paths.get(fName), s.toString().getBytes(), StandardOpenOption.APPEND); 193 } 194 195 /** 196 * This takes an {@code Iterable<String>}, and a filename, and appends each 197 * {@code java.lang.String} in the {@code Iterator} that it produces to the file 198 * specified by File-Name parameter {@code 'fName'} 199 * 200 * <BR /><BR /><DIV CLASS=JDHint> 201 * <B STYLE='color:red;'>Directory Requirements:</B> Though the file does not need to exist 202 * already in order for this file to be written the directory hierarchy needs to exist, or a 203 * Java {@code 'IOException'} will occur. 204 * </DIV> 205 * 206 * @param i This is any java {@code Iterable<String>} object. Each of these will be written, 207 * in succession, to the file named by parameter {@code 'fName'} 208 * 209 * @param fName The name of the file to which the contents of the {@code java.lang.String} 210 * are appended. If This file doesn't already exist, it is created here. 211 * 212 * @param addNewLines When this parameter is passed {@code TRUE}, a New-Line character will be 213 * appended after each {@code String} that is written. When {@code FALSE}, only the 214 * {@code String's} produced by the {@code Iterable}, themselves, are appended to the file. 215 * 216 * @throws IOException If an I/O error has occurred as a result of the File-System or 217 * disk operation. 218 */ 219 public static void appendToFile(Iterable<String> i, String fName, boolean addNewLines) 220 throws IOException 221 { 222 File f = new File(fName); 223 224 if (! f.exists()) f.createNewFile(); 225 226 227 // Uses the "Ternary Operator" / "Conditional Operator" Syntax, but it's a little hard to 228 // read that. There is a '?' and a ':' after the boolean 'addNewsLines' 229 230 Iterable<String> passedIterable = addNewLines 231 232 233 // If the user has requested to add newlines, wrap the passed Iterable inside of a new 234 // Iterable that appends a new-line character to the output of the User's 'next()' 235 // method (which is just returning the String's to be written to disk - less the newline) 236 237 ? new Iterable<>() 238 { 239 private final Iterator<String> iterInternal = i.iterator(); 240 241 242 // Funny Note: All an "Iterable" is is an Object that returns an Iterator. The 243 // interface "Iterable" only has one non-Default Method - iterator(). Re-Write 244 // that method to return a slightly altered Iterator<String> 245 246 public Iterator<String> iterator() 247 { 248 return new Iterator<String>() 249 { 250 public boolean hasNext() { return iterInternal.hasNext(); } 251 public String next() { return iterInternal.next() + '\n'; } 252 }; 253 } 254 } 255 256 // Otherwise, just assign the Users Iterable to the "passedIterable" Variable 257 : i; 258 259 // Now write the Passed Iterable of String, using java.nio.file.Files 260 Files.write(Paths.get(fName), passedIterable, StandardOpenOption.APPEND); 261 } 262 263 264 // ******************************************************************************************** 265 // ******************************************************************************************** 266 // Load File (as String's} 267 // ******************************************************************************************** 268 // ******************************************************************************************** 269 270 271 /** 272 * This will load the entire contents of a Text-File on disk to a single 273 * {@code java.lang.String} 274 * 275 * <BR /><BR /><DIV CLASS=JDHint> 276 * <B STYLE='color:red;'>Trailing Zeros:</B> Some of the ugliest code to see is that which 277 * scans for {@code 'EOF'} characters that have been liberally inserted into a simple 278 * Text-File. When reading a file (which, regardless of whether it <B>actually is a 279 * Text-File</B>), this method will remove any <B>trailing {@code ASCII-0}</B> characters 280 * (literally, {@code char c == 0}) from the files that are read. 281 * </DIV> 282 * 283 * <BR />Finding {@code '.html'} or {@code '.java'} files in which some editor (for whatever 284 * reason) has inserted {@code EOF-like} characters to the end of the text can make programming 285 * a headache. 286 * 287 * <BR /><BR />Suffice it to say, the {@code String} that is returned from this method will 288 * contain the last non-zero character (including CRLF, {@code '\n'} or {@code '\r'} 289 * character that was read from the file. Operating-systems do not require that a file have a 290 * trailing zero to interpret them. 291 * 292 * <BR /><BR />Character {@code '0'} is a legacy / older-version of the EOF Marker. 293 * {@code '.java'}-Files certainly don't need them, and they can actually be a problem when a 294 * developer is checking for file's that have equal-{@code String's} in them. 295 * 296 * <BR /><BR /><DIV CLASS=JDHintAlt> 297 * <B STYLE='color:red;'>Static Boolean-Flag:</B> This class (class {@code FileRW}) has a 298 * {@code static, boolean} flag that is able to prevent / shunt this 'Trailing Zero Removing' 299 * behavior. When {@link #TRUNCATE_EOF_CHARS} is set to {@code FALSE}, reading a file into a 300 * {@code String} using this method will return the {@code String} - <B>including as many 301 * Trailing Zero-Characters as have been appended to the end of that file</B>. 302 * </DIV> 303 * 304 * @param fName the File-Name of a valid Text-File in the File-System 305 * 306 * @return The entire contents of the file as a {@code String}. 307 * 308 * @throws IOException If an I/O error has occurred as a result of the File-System or 309 * disk operation. 310 */ 311 public static String loadFileToString(String fName) throws IOException 312 { 313 // The reader is 'java.lang.AutoCloseable'. 314 // 315 // NOTE: The IOException will still be thrown out of this method if it occurs. It is not 316 // caught! 317 318 try 319 (FileReader fr = new FileReader(fName)) 320 { 321 int len = (int) new File(fName).length(); 322 char[] cArr = new char[len]; 323 int offset = 0; 324 int charsRead = 0; 325 int charsRemaining = len; 326 327 while ((offset < len) && ((charsRead = fr.read(cArr, offset, charsRemaining)) != -1)) 328 { 329 offset += charsRead; 330 charsRemaining -= charsRead; 331 } 332 333 len = cArr.length; 334 335 if (TRUNCATE_EOF_CHARS) while ((len > 0) && (cArr[len-1] == 0)) len--; 336 337 return (len != cArr.length) ? new String(cArr, 0, len) : new String(cArr); 338 } 339 } 340 341 /** 342 * Convenience Method. 343 * <BR />Invokes: {@link #loadFileToStream(String, boolean)} 344 * <BR />Converts: {@code Stream<String>} into {@code String[]} 345 */ 346 public static String[] loadFileToStringArray(String fName, boolean includeNewLine) 347 throws IOException 348 { return loadFileToStream(fName, includeNewLine).toArray(String[]::new); } 349 350 /** 351 * This will load a file to a Java {@code Stream} instance. 352 * 353 * @param fName A File-Name of a valid Text-File in the File-System. 354 * 355 * @param includeNewLine if this is {@code TRUE}, a {@code '\n'} (newline/CRLF) is appended to 356 * the end of each {@code java.lang.String} read from this file. If not, the original newline 357 * characters which occur in the file, will be eliminated. 358 * 359 * <BR /><BR /><DIV CLASS=JDHint> 360 * This method will make one (potentially minor) mistake. If the final character in the 361 * input-file is, itself, a new-line (if the file ends with a {@code 'CRLF' / 'CR'}), then this 362 * method should return a {@code Stream<String>} that is identical to the original file. 363 * However, <B>if the final character in the file <SPAN STYLE='color:red;'>is not</SPAN> a 364 * new-line {@code '\n'}</B>, then the {@code Stream<String>} that is returned will have an 365 * extra new-line appended to the last {@code String} in the {@code Stream}, and the resultant 366 * {@code Stream<String>} will be longer than the original file by 1 character. 367 * </DIV> 368 * 369 * @return The entire contents of the file as a series of {@code java.lang.String} contained by 370 * a {@code java.util.stream.Stream<String>}. Converting Java {@code Stream's} to other data 371 * container types is as follows: 372 * 373 * <EMBED CLASS='external-html' DATA-FILE-ID=STRMCNVT> 374 * 375 * @throws IOException If an I/O error has occurred as a result of the File-System or disk 376 * operation. 377 */ 378 public static Stream<String> loadFileToStream(String fName, boolean includeNewLine) 379 throws IOException 380 { 381 // These readers's are 'java.lang.AutoCloseable'. 382 // 383 // NOTE: The IOException will still be thrown out of this method if it occurs. It is not 384 // caught! 385 // 386 // ALSO: In try-with-resources blocks, if there is a problem/exception, these 387 // classes are all (automatically) closed/flushed, in reverse order. 388 389 try ( 390 FileReader fr = new FileReader(fName); 391 BufferedReader br = new BufferedReader(fr); 392 ) 393 { 394 Stream.Builder<String> b = Stream.builder(); 395 String s = ""; 396 397 if (includeNewLine) 398 while ((s = br.readLine()) != null) b.add(s + "\n"); 399 400 else 401 while ((s = br.readLine()) != null) b.add(s); 402 403 return b.build(); 404 } 405 } 406 407 /** 408 * Convenience Method. 409 * <BR />Invokes: {@link #loadFileToCollection(Collection, String, boolean)} 410 * <BR />Passes: Newly instantiated {@code Vector<String>} to {@code 'collectionChoice'} 411 */ 412 public static Vector<String> loadFileToVector(String fName, boolean includeNewLine) 413 throws IOException 414 { return loadFileToCollection(new Vector<String>(), fName, includeNewLine); } 415 416 /** 417 * This method loads the contents of a file to a {@code java.util.Collection<String>} object, 418 * where each {@code java.lang.String} in the output / returned {@code Collection} is a 419 * different "line of text" from the input-file. This is identical to invoking: 420 * 421 * <DIV CLASS=LOC>{@code 422 * Collection<String> myTextFile = FileRW.loadFileToString("someFile.txt").split('\n') 423 * }</DIV> 424 * 425 * <BR /><DIV CLASS=JDHint> 426 * <B STYLE='color:red;'>Variable-Type Parameter:</B> This method uses Java's Variable-Type 427 * Parameter syntax to allow the programmer to decide what type of {@code Collection<String>} 428 * they would like returned from this method. Common examples would include 429 * {@code Vector<String>, ArrayList<String>, HashSet<String>}, etc... 430 * </DIV> 431 * 432 * <BR /><B CLASS=JDDescLabel>Loading EOF:</B> 433 * 434 * <BR />This method will make one small mistake. If the final character inside the input-file 435 * is, itself, a new-line, then this method will return a Java {@code String-Collection} which 436 * is is identical to the original file. 437 * 438 * <BR /><BR />However, <I>if the final character in the file <B>is not</B> a new-line 439 * {@code '\n'} character</I>, then the returned {@code Collection} will have an extra new-line 440 * appended immediately after the last {@code String} in the {@code Collection}. Furthermore, 441 * the resultant {@code Collection<String>} will be longer than the original file by 1 character. 442 * 443 * @param collectionChoice This must be an instance of a class that extends Java's 444 * base {@code class Collection<String>} - <I>using {@code 'String'} as the generic-type 445 * parameter.</I> It will be populated with the lines from a Text-File using the 446 * {@code Collection.add(String)} method. 447 * 448 * @param <T> This may be any class which extends {@code java.util.Collection}. It is 449 * specified as a "Type Parameter" because this collection is returned as a result of this 450 * function. Perhaps it is superfluous to return the same reference that is provided by the 451 * user as input to this method, but this certainly doesn't change the method signature or 452 * make it more complicated. 453 * 454 * @param fName the File-Name of a valid Text-File on the File-System. 455 * 456 * @param includeNewLine if this is {@code TRUE}, a {@code '\n'} (newline/CRLF) is appended to 457 * the end of each {@code java.lang.String} read from this file. If not, the original newline 458 * characters which occur in the file, will be eliminated. 459 * 460 * @return An identical reference to the reference passed to parameter 461 * {@code 'collectionChoice'} 462 * 463 * @throws IOException If an I/O error has occurred as a result of the File-System or disk 464 * operation. 465 */ 466 public static <T extends Collection<String>> T loadFileToCollection 467 (T collectionChoice, String fName, boolean includeNewLine) 468 throws IOException 469 { 470 // These readers's are 'java.lang.AutoCloseable'. 471 // 472 // NOTE: The IOException will still be thrown out of this method if it occurs. It is not 473 // caught! 474 // 475 // ALSO: In try-with-resources blocks, if there is a problem/exception, these 476 // classes are all (automatically) closed/flushed, in reverse order. 477 478 try ( 479 FileReader fr = new FileReader(fName); 480 BufferedReader br = new BufferedReader(fr); 481 ) 482 { 483 String s = ""; 484 485 if (includeNewLine) 486 while ((s = br.readLine()) != null) collectionChoice.add(s + "\n"); 487 488 else 489 while ((s = br.readLine()) != null) collectionChoice.add(s); 490 } 491 492 return collectionChoice; 493 } 494 495 496 // ******************************************************************************************** 497 // ******************************************************************************************** 498 // Write ONE Object To File 499 // ******************************************************************************************** 500 // ******************************************************************************************** 501 502 503 /** 504 * Convenience Method. 505 * <BR />Invokes: {@link #writeObjectToFile(Object, String, boolean)} 506 * <BR />Catches Exception 507 */ 508 public static boolean writeObjectToFileNOCNFE(Object o, String fName, boolean ZIP) 509 throws IOException 510 { 511 try 512 { writeObjectToFile(o, fName, ZIP); return true; } 513 514 catch (ClassNotFoundException cnfe) { return false; } 515 } 516 517 /** 518 * Writes a {@code java.lang.Object} to a file for storage, and future reference. 519 * 520 * <EMBED CLASS='external-html' DATA-FILE-ID=FRW_WRITABLE_DIR> 521 * 522 * @param o An {@code Object} to be written to a file as a <I><B>"Serializable"</B></I> 523 * {@code java.lang.Object} 524 * 525 * @param fName The name of the output file 526 * 527 * @param ZIP a boolean that, when {@code TRUE}, will cause the object's data to be compressed 528 * before being written to the output file. 529 * 530 * @throws IOException If an I/O error has occurred as a result of the File-System or disk 531 * operation. 532 * 533 * @throws ClassNotFoundException This exception is thrown when the Java Virtual 534 * Machine (JVM) tries to load a particular class and the specified class cannot be found in 535 * the classpath. 536 */ 537 public static void writeObjectToFile(Object o, String fName, boolean ZIP) 538 throws IOException, ClassNotFoundException 539 { 540 // These stream's are 'java.lang.AutoCloseable'. 541 // 542 // NOTE: The IOException and ClassNotFoundException will still be thrown out of this 543 // method if they occur. They are not caught! 544 // 545 // ALSO: In try-with-resources blocks, if there is a problem/exception, these 546 // classes are all (automatically) closed/flushed, in reverse order. 547 548 if (ZIP) 549 550 try ( 551 FileOutputStream fos = new FileOutputStream(fName); 552 GZIPOutputStream gzip = new GZIPOutputStream(fos); 553 ObjectOutputStream oos = new ObjectOutputStream(gzip); 554 ) 555 { oos.writeObject(o); } 556 557 else 558 559 try ( 560 FileOutputStream fos = new FileOutputStream(fName); 561 ObjectOutputStream oos = new ObjectOutputStream(fos); 562 ) 563 { oos.writeObject(o); } 564 } 565 566 567 // ******************************************************************************************** 568 // ******************************************************************************************** 569 // Write ALL Objects ToFile 570 // ******************************************************************************************** 571 // ******************************************************************************************** 572 573 574 /** 575 * Convenience Method. 576 * <BR />Invokes: {@link #writeAllObjectsToFile(Iterable, String, boolean)} 577 * <BR />Catches Exception 578 */ 579 public static boolean writeAllObjectsToFileNOCNFE(Iterable<?> i, String fName, boolean ZIP) 580 throws IOException 581 { 582 try 583 { writeAllObjectsToFile(i, fName, ZIP); return true; } 584 585 catch (ClassNotFoundException cnfe) { return false; } 586 } 587 588 /** 589 * Writes a series of {@code java.lang.Object} to a file for storage, and future reference. 590 * 591 * <EMBED CLASS='external-html' DATA-FILE-ID=FRW_1OBJ_FILE> 592 * <EMBED CLASS='external-html' DATA-FILE-ID=FRW_WRITABLE_DIR> 593 * 594 * @param i A series, {@code Collection}, or {@code List} of {@code Object's} to be written 595 * to a file in <I><B>Serializable</B></I> format. 596 * 597 * @param fName The name of the output file 598 * 599 * @param ZIP a {@code boolean} that, when {@code TRUE}, will cause the {@code Object's} 600 * data to be compressed before being written to the output-file. 601 * 602 * @throws IOException If an I/O error has occurred as a result of the File-System or 603 * disk operation. 604 * 605 * @throws ClassNotFoundException This exception is thrown when the Java Virtual 606 * Machine (JVM) tries to load a particular class and the specified class cannot be found in 607 * the {@code CLASSPATH}. 608 */ 609 public static void writeAllObjectsToFile(Iterable<?> i, String fName, boolean ZIP) 610 throws IOException, ClassNotFoundException 611 { 612 Iterator<?> iter = i.iterator(); 613 614 // These stream's are 'java.lang.AutoCloseable'. 615 // 616 // NOTE: The IOException and ClassNotFoundException will still be thrown out of this 617 // method if they occur. They are not caught! 618 // 619 // ALSO: In try-with-resources blocks, if there is a problem/exception, these 620 // classes are all (automatically) closed/flushed, in reverse order. 621 622 if (ZIP) 623 624 try ( 625 FileOutputStream fos = new FileOutputStream(fName); 626 GZIPOutputStream gzip = new GZIPOutputStream(fos); 627 ObjectOutputStream oos = new ObjectOutputStream(gzip); 628 ) 629 { while (iter.hasNext()) oos.writeObject(iter.next()); } 630 631 else 632 633 try ( 634 FileOutputStream fos = new FileOutputStream(fName); 635 ObjectOutputStream oos = new ObjectOutputStream(fos); 636 ) 637 { while (iter.hasNext()) oos.writeObject(iter.next()); } 638 } 639 640 641 // ******************************************************************************************** 642 // ******************************************************************************************** 643 // read ONE Object From File 644 // ******************************************************************************************** 645 // ******************************************************************************************** 646 647 648 /** 649 * Convenience Method. 650 * <BR />Invokes: {@link #readObjectFromFile(String, boolean)} 651 * <BR />Catches Exception 652 */ 653 public static Object readObjectFromFileNOCNFE(String fName, boolean ZIP) throws IOException 654 { 655 try 656 { return readObjectFromFile(fName, ZIP); } 657 658 catch (ClassNotFoundException cnfe) { return null; } 659 } 660 661 /** 662 * Reads an {@code Object} from a Data-File which must contain a serialized 663 * {@code java.lang.Object}. 664 * 665 * <DIV CLASS="EXAMPLE">{@code 666 * // Create some Object for writing to the File-System, using Object Serialization 667 * int[] dataArr = some_data_method(); 668 * 669 * // It is always easier to pass 'true' to the compression boolean parameter 670 * FileRW.writeObjectToFile(dataArr, "data/myDataFile.dat", true); 671 * 672 * ... 673 * 674 * // Later on, this file may be read back into the program, using this call: 675 * Object o = FileRW.readObjectFromFile("data/myDataFile.dat", true); 676 * 677 * // This check prevents compiler-time warnings. The Annotation "SuppressWarnings" 678 * // would also work. 679 * dataArr = (o instanceof int[]) ? (int[]) o : null; 680 * }</DIV> 681 * 682 * <EMBED CLASS='external-html' DATA-FILE-ID=FRW_1OBJ_FILE> 683 * 684 * @param fName The name of a Data-File that contains a serialized {@code java.lang.Object} 685 * 686 * @param ZIP if this is {@code TRUE}, it is assumed that the Data-File contains a 687 * Zip-Compressed {@code Object} 688 * 689 * @return The {@code Object} that was written to the Data-File. 690 * 691 * @throws IOException If an I/O error has occurred as a result of the File-System or 692 * disk operation. 693 * 694 * @throws ClassNotFoundException This exception is thrown when the Java Virtual 695 * Machine (JVM) tries to load a particular class and the specified class cannot be found 696 * in the {@code CLASSPATH}. 697 */ 698 public static Object readObjectFromFile(String fName, boolean ZIP) 699 throws IOException, ClassNotFoundException 700 { 701 // These stream's are 'java.lang.AutoCloseable'. 702 // 703 // NOTE: The IOException and ClassNotFoundException will still be thrown out of this 704 // method if they occur. They are not caught! 705 // 706 // ALSO: In try-with-resources blocks, if there is a problem/exception, these 707 // classes are all (automatically) closed/flushed, in reverse order. 708 709 try ( 710 FileInputStream fis = new FileInputStream(fName); 711 ObjectInputStream ois = ZIP 712 ? new ObjectInputStream(new GZIPInputStream(fis)) 713 : new ObjectInputStream(fis); 714 ) 715 { return ois.readObject(); } 716 } 717 718 /** 719 * Convenience Method. 720 * <BR />Invokes: {@link #readObjectFromFile(String, Class, boolean)} 721 * <BR />Catches Exception 722 */ 723 public static <T> T readObjectFromFileNOCNFE(String fName, Class<T> c, boolean ZIP) 724 throws IOException 725 { 726 try 727 { return readObjectFromFile(fName, c, ZIP); } 728 729 catch (ClassNotFoundException cnfe) { return null; } 730 } 731 732 /** 733 * Reads an {@code Object} from a Data-File which must contain a serialized 734 * {@code java.lang.Object}. 735 * 736 * <EMBED CLASS='external-html' DATA-FILE-ID=FRW_1OBJ_FILE> 737 * 738 * @param fName The name of a Data-File that contains a serialized {@code java.lang.Object} 739 * 740 * @param c This is the type of the {@code Object} expecting to be read from disk. A value 741 * for this parameter can always be obtained by referencing the {@code static} field 742 * {@code '.class'} which is attached to <I>every object</I> in java. 743 * 744 * <EMBED CLASS='external-html' DATA-FILE-ID=FRW_CLASS_T> 745 * 746 * @param <T> This should be set to the return type of this method. By passing the expecred 747 * return-type class, you may conveniently avoid having to cast the returned instance, or worry 748 * about suppressing compiler warnings. 749 * 750 * @param ZIP if this is {@code TRUE}, it is assumed that the Data-File contains a 751 * Zip-Compressed {@code Object} 752 * 753 * @return The {@code Object} that was read from the Data-File. 754 * 755 * @throws IOException If an I/O error has occurred as a result of the File-System or disk 756 * operation. 757 * 758 * @throws ClassNotFoundException This exception is thrown when the Java Virtual 759 * Machine (JVM) tries to load a particular class and the specified class cannot be found in 760 * the {@code CLASSPATH}. 761 */ 762 public static <T> T readObjectFromFile(String fName, Class<T> c, boolean ZIP) 763 throws IOException, ClassNotFoundException 764 { 765 Object o = readObjectFromFile(fName, ZIP); 766 767 if (o == null) return null; 768 if (c.isInstance(o)) return c.cast(o); 769 770 throw new ClassNotFoundException( 771 "Although an object was indeed read from the file you have named [" + fName + "], " + 772 "that object was not an instance of [" + c.getName() + "], " + 773 "but rather of [" + o.getClass().getName() + "]" 774 ); 775 } 776 777 778 // ******************************************************************************************** 779 // ******************************************************************************************** 780 // read ALL OBJECTS FromFile 781 // ******************************************************************************************** 782 // ******************************************************************************************** 783 784 785 /** 786 * Convenience Method. 787 * <BR />Invokes: {@link #readAllObjects(Class, Collection, String, boolean)} 788 * <BR />Passes: {@code Object.class} to {@code 'objType'} 789 * <BR />Passes: Newly instantiated {@code Vector<Object>} to {@code 'collection'} 790 */ 791 public static Vector<Object> readAllObjectsToVector(String fName, boolean ZIP) 792 throws IOException, ClassNotFoundException 793 { return readAllObjects(Object.class, new Vector<Object>(), fName, ZIP); } 794 795 /** 796 * Convenience Method. 797 * <BR />Invokes: {@link #readAllObjects(Class, Collection, String, boolean)} 798 * <BR />Passes: Newly instantiated {@code Vector<T>} to {@code 'collection'} 799 */ 800 public static <T> Vector<T> readAllObjectsToVector(Class<T> objType, String fName, boolean ZIP) 801 throws IOException, ClassNotFoundException 802 { return readAllObjects(objType, new Vector<T>(), fName, ZIP); } 803 804 /** 805 * Reads all {@code Object's} found inside a Data-File. This Data-File should contain only 806 * java-serialized {@code java.lang.Object's}. 807 * 808 * @param objType <EMBED CLASS='external-html' DATA-FILE-ID=FRW_OBJTYPE_PARAM> 809 * 810 * @param <T> This allows the programmer to inform this method what type of {@code Object's} 811 * are stored in the Serialized Object File, specified by {@code 'fName'}. 812 * 813 * @param collection This should be the desired {@code Collection<E>} that the programmer 814 * would like be populated with the instances of type {@code 'objType'} read from file 815 * {@code 'fName'}. Variable-Type parameter {@code 'E'} needs to be equal-to or an 816 * ancestor of {@code 'objType'} 817 * 818 * @param <U> Merely for convenience, this method returns the {@code Collection} instance that 819 * is passed (by parameter {@code 'Collection'}) as a result of this function. Therefore, the 820 * Type Parameter {@code 'U'} identifies the Return Type of this method. 821 * 822 * @param fName The name of a Data-File that contains serialized {@code Object's} 823 * 824 * @param ZIP if this is {@code TRUE}, it is assumed that the Data-File contains Zip-Compressed 825 * objects 826 * 827 * @return A reference to the {@code Collection} that was passed to this method. 828 * 829 * @throws IOException If an I/O error has occurred as a result of the File-System or disk 830 * operation. 831 */ 832 public static <T, U extends Collection<T>> U readAllObjects 833 (Class<T> objType, U collection, String fName, boolean ZIP) 834 throws IOException, ClassNotFoundException 835 { 836 // Temporary Variable, used in both versions of this method 837 Object o; 838 839 // These stream's are 'java.lang.AutoCloseable'. 840 // 841 // NOTE: The IOException and ClassNotFoundException will still be thrown out of this 842 // method if they occur. They are not caught! 843 // 844 // ALSO: In try-with-resources blocks, if there is a problem/exception, these 845 // classes are all (automatically) closed/flushed, in reverse order. 846 847 if (ZIP) 848 849 try ( 850 FileInputStream fis = new FileInputStream(fName); 851 GZIPInputStream gis = new GZIPInputStream(fis); 852 ObjectInputStream ois = new ObjectInputStream(gis); 853 ) 854 { 855 while ((o = ois.readObject()) != null) 856 857 if (objType.isInstance(o)) 858 collection.add(objType.cast(o)); 859 860 else throw new ClassNotFoundException( 861 "At least one of the objects in the serialized object file " + 862 "[" + fName + "], " + 863 "was not an instance of [" + objType.getName() + "], " + 864 "but rather [" + o.getClass().getName() + "]" 865 ); 866 } 867 868 catch (EOFException eofe) { } 869 870 else 871 872 try ( 873 FileInputStream fis = new FileInputStream(fName); 874 ObjectInputStream ois = new ObjectInputStream(fis); 875 ) 876 { 877 while ((o = ois.readObject()) != null) 878 879 if (objType.isInstance(o)) 880 collection.add(objType.cast(o)); 881 882 else throw new ClassNotFoundException( 883 "At least one of the objects in the serialized object file " + 884 "[" + fName + "], " + 885 "was not an instance of [" + objType.getName() + "], " + 886 "but rather [" + o.getClass().getName() + "]" 887 ); 888 } 889 890 catch (EOFException eofe) { } 891 892 return collection; 893 } 894 895 /** 896 * Convenience Method. 897 * <BR />Invokes: {@link #readAllObjectsToStream(Class, String, boolean)} 898 * <BR />Passes: {@code Object.class} to {@code 'objType'} 899 */ 900 public static Stream<Object> readAllObjectsToStream(String fName, boolean ZIP) 901 throws IOException, ClassNotFoundException 902 { return readAllObjectsToStream(Object.class, fName, ZIP); } 903 904 /** 905 * Reads all objects found inside a Data-File. This Data-File should contain only 906 * java-serialized {@code Object's}. 907 * 908 * @param fName The name of a Data-File that contains the serialized {@code Object's} 909 * 910 * @param objType This is the type of the {@code Object's} expecting to be read from disk. A 911 * value for this parameter can always be obtained by referencing the static field 912 * {@code '.class'} which is attached to <I>every {@code Object}</I> in Java. For instance, to 913 * read a Data-File containing a series of {@code Date} instances, use {@code Date.class} as the 914 * value to pass to this parameter. 915 * 916 * @param <T> This parameter informs this method what type of Serialized Objects are saved 917 * within {@code 'fName'}. The Java {@code Stream} that is returned as a result of this method 918 * will have the type {@code Stream<T>}. 919 * 920 * @param ZIP if this is {@code TRUE}, it is assumed that the Data-File contains Zip-Compressed 921 * {@code Object's} 922 * 923 * @return A {@code Stream<T>} of all {@code Object's} found in the Data-File. Converting 924 * Java {@code Stream's} to other Data-Container types is as follows: 925 * 926 * <EMBED CLASS='external-html' DATA-FILE-ID=STREAM_CONVERT_T> 927 * 928 * @throws IOException If an I/O error has occurred as a result of the File-System or disk 929 * operation. 930 * 931 * @throws ClassNotFoundException This exception is thrown when the Java Virtual 932 * Machine (JVM) tries to load a particular class and the specified class cannot be found in 933 * the classpath. 934 */ 935 public static <T> Stream<T> readAllObjectsToStream(Class<T> objType, String fName, boolean ZIP) 936 throws IOException, ClassNotFoundException 937 { 938 Stream.Builder<T> b = Stream.builder(); 939 Object o = null; 940 941 try( 942 FileInputStream fis = new FileInputStream(fName); 943 944 ObjectInputStream ois = ZIP 945 ? new ObjectInputStream(new GZIPInputStream(fis)) 946 : new ObjectInputStream(fis); 947 ) 948 { 949 while ((o = ois.readObject()) != null) 950 951 if (objType.isInstance(o)) b.accept(objType.cast(o)); 952 953 else throw new ClassNotFoundException( 954 "At least one of the objects in the serialized object file [" + fName + "], " + 955 "was not an instance of [" + objType.getName() + "], " + 956 "but rather [" + o.getClass().getName() + "]" 957 ); 958 } 959 960 catch (EOFException eofe) { } 961 962 return b.build(); 963 } 964 965 966 // ******************************************************************************************** 967 // ******************************************************************************************** 968 // Base-64 Read / Write Stuff (Text File) 969 // ******************************************************************************************** 970 // ******************************************************************************************** 971 972 973 /** 974 * Uses Java's {@code Object} serialization mechanism to serialize a {@code java.lang.Object}, 975 * and then uses the {@code Base64 String-MIME Encoding} system, also provided by java, to 976 * convert the {@code Object} into a text-safe {@code java.lang.String} that may be viewed, 977 * e-mailed, written to a web-page, etc. 978 * 979 * <EMBED CLASS='external-html' DATA-FILE-ID=FRW_1OBJ_FILE> 980 * <EMBED CLASS='external-html' DATA-FILE-ID=FRW_EXAMPLE_01> 981 * 982 * @param o This may be any serializable {@code java.lang.Object} instance. It will be written 983 * to a Text-File after first serializing the {@code Object}, and then next converting the 984 * serialized data-bits to MIME-safe encoded text. 985 * 986 * @param fileName The fileName that will be used to save this {@code Object} as a Text-File. 987 * 988 * @see #writeFile(CharSequence, String) 989 * @see StringParse#objToB64MimeStr(Object) 990 */ 991 public static void writeObjectToTextFile(Object o, String fileName) 992 throws IOException 993 { FileRW.writeFile(StringParse.objToB64MimeStr(o), fileName); } 994 995 /** 996 * This will read a java serialized, and MIME-Converted, MIME-Safe {@code java.lang.String} 997 * from a text file and return the {@code Object} that it represented 998 * 999 * <EMBED CLASS='external-html' DATA-FILE-ID=FRW_1OBJ_FILE> 1000 * 1001 * @param fileName The name of the file containing the MIME-Encoded Serialized 1002 * {@code java.lang.Object}. 1003 * 1004 * @return The {@code Object} that had been encoded into the Text-File. 1005 * 1006 * @see #loadFileToString(String) 1007 * @see StringParse#b64MimeStrToObj(String) 1008 */ 1009 public static Object readObjectFromTextFile(String fileName) throws IOException 1010 { return StringParse.b64MimeStrToObj(loadFileToString(fileName)); } 1011 1012 /** 1013 * Convenience Method. 1014 * <BR />Invokes: {@link #readObjectFromTextFile(String, Class)} 1015 * <BR />Catches Exception 1016 */ 1017 public static <T> T readObjectFromTextFileNOCNFE(String fileName, Class<T> c) 1018 throws IOException 1019 { 1020 try 1021 { return readObjectFromTextFile(fileName, c); } 1022 1023 catch (ClassNotFoundException cnfe) { return null; } 1024 } 1025 1026 /** 1027 * This will read a java serialized, and MIME-Converted, MIME-Safe {@code java.lang.String} 1028 * from a text file and return the {@code Object} that it represented 1029 * 1030 * <EMBED CLASS='external-html' DATA-FILE-ID=FRW_1OBJ_FILE> 1031 * <EMBED CLASS='external-html' DATA-FILE-ID=FRW_EXAMPLE_01> 1032 * 1033 * @param fileName The name of the file containing the MIME-Encoded Serialized 1034 * {@code java.lang.Object}. 1035 * 1036 * @param c This is the type of the {@code Object} expecting to be read from disk. A value 1037 * for this parameter can always be obtained by referencing the {@code static} field 1038 * {@code '.class'}, which is attached to <I>every {@code Object}</I> in java. 1039 * 1040 * <EMBED CLASS='external-html' DATA-FILE-ID=FRW_CLASS_T> 1041 * 1042 * @param <T> This Type Parameter informs this method what type of Base-64 Serialized Object 1043 * is saved within Text File {@code 'fileName'}. The returned result of this method will have 1044 * this type, for convenience to avoid casting the {@code Object} (<I>unless that 1045 * {@code Object} is a Java Generic, and you wish to avoid a "Raw Types" warning</I>. See 1046 * further details inside the explanation about parameter {@code 'c'}). 1047 * 1048 * @return The {@code Object} that had been encoded into the Text-File. 1049 * 1050 * @throws IOException If an I/O error has occurred as a result of the File-System or disk 1051 * operation. 1052 * 1053 * @throws ClassNotFoundException This exception is thrown when the Java Virtual 1054 * Machine (JVM) tries to load a particular class and the specified class cannot be found in 1055 * the {@code CLASSPATH}. 1056 * 1057 * @see StringParse#b64MimeStrToObj(String) 1058 * @see #loadFileToString(String) 1059 */ 1060 public static <T> T readObjectFromTextFile(String fileName, Class<T> c) 1061 throws IOException, ClassNotFoundException 1062 { 1063 Object o = StringParse.b64MimeStrToObj(loadFileToString(fileName)); 1064 1065 if (o == null) return null; 1066 if (c.isInstance(o)) return c.cast(o); 1067 1068 throw new ClassNotFoundException( 1069 "Although an object was indeed read from the file you have named [" + fileName + "], " + 1070 "that object was not an instance of [" + c.getName() + "], " + 1071 "but rather of [" + o.getClass().getName() + "]" 1072 ); 1073 } 1074 1075 1076 // ******************************************************************************************** 1077 // ******************************************************************************************** 1078 // Object Input & Object Output Streams 1079 // ******************************************************************************************** 1080 // ******************************************************************************************** 1081 1082 1083 /** 1084 * Creates a simple {@code ObjectInputStream} - usually if multiple {@code Object's} have been 1085 * written to a single file. It was better practice to put {@code Object's} in a 1086 * {@code java.util.Vector}, and write one {@code java.util.Vector} during serialization. 1087 * 1088 * <BR /><BR />This, eventually, can became inadequate when downloading large numbers of HTML 1089 * results, where the need to write a large Data-File (intermittently - by saving intermediate 1090 * results) is needed. 1091 * 1092 * @param fName This is the File-Name of the Data-File where the serialized {@code Object's} 1093 * have been stored. 1094 * 1095 * @param ZIP If this is set to {@code TRUE}, the data will be de-compressed. 1096 * 1097 * @return A java {@code ObjectInputStream} 1098 * 1099 * @throws IOException If an I/O error has occurred as a result of the File-System 1100 * or disk operation. 1101 */ 1102 public static ObjectInputStream getOIS(String fName, boolean ZIP) throws IOException 1103 { 1104 return ZIP 1105 ? new ObjectInputStream(new GZIPInputStream(new FileInputStream(new File(fName)))) 1106 : new ObjectInputStream(new FileInputStream(new File(fName))); 1107 } 1108 1109 /** 1110 * Creates a simple {@code ObjectOutputStream} - usually if multiple {@code Object's} need 1111 * to be written to a single file. It was better practice to put {@code Object's} in a 1112 * {@code java.util.Vector}, and write one {@code java.util.Vector} during serialization. 1113 * 1114 * @param fName This is the File-Name of the Data-File where the serialized {@code Object's} 1115 * will be stored. 1116 * 1117 * @param ZIP If this is set to {@code TRUE}, the data will be compressed. 1118 * 1119 * @return A java {@code ObjectInputStream} 1120 * 1121 * @throws IOException If an I/O error has occurred as a result of the File-System or 1122 * disk operation. 1123 */ 1124 public static ObjectOutputStream getOOS(String fName, boolean ZIP) throws IOException 1125 { 1126 return ZIP 1127 ? new ObjectOutputStream(new GZIPOutputStream(new FileOutputStream(new File(fName)))) 1128 : new ObjectOutputStream(new FileOutputStream(new File(fName))); 1129 } 1130 1131 1132 // ******************************************************************************************** 1133 // ******************************************************************************************** 1134 // Copy, Move, Delete 1135 // ******************************************************************************************** 1136 // ******************************************************************************************** 1137 1138 1139 /** 1140 * This method will perform a byte-for-byte copy of a file from one location to another. 1141 * 1142 * @param inFileName The name of the input-file. It will be byte-for-byte copied to an output 1143 * File-Name. 1144 * 1145 * @param outFileOrDirName The name of the output-file, or the name of the directory where the 1146 * file shall be moved. 1147 * 1148 * <BR /><BR />If this file already exists <I>and it is a file (not a directory)</I> 1149 * it will be over-written. 1150 * 1151 * <BR /><BR />If parameter {@code 'outFileOrDirName'} names a directory - <I>due 1152 * to having an ending {@code File.separator}</I>, <B>but</B> does not appear to name a 1153 * directory that exists, this method will throw a {@code FileNotFoundException}. 1154 * 1155 * <BR /><BR />This can be avoided, however, by passing {@code TRUE} to parameter 1156 * {@code 'createDirsIfNotExist'}. If {@code 'outFileOrDirName'} specifies a directory (by 1157 * virtue of the fact that it ends with the {@code File.separator String}) - <I>and 1158 * {@code 'createDirsIfNotExist'} is {@code TRUE}</I>, then this method will first create any 1159 * and all sub-directories needed using the standard Java's {@code File.mkdirs()} method before 1160 * copying the file. 1161 * 1162 * <BR /><BR /><DIV CLASS=JDHint> 1163 * <B><SPAN STYLE="color: red;">Summary:</SPAN></B> The programmer may provide either a 1164 * Directory-Name or a File-Name to parameter {@code 'outFileOrDirName'}. If a Directory-Name 1165 * was provided, the moved file will <B>keep its name</B> - having the same name as the 1166 * original file, ({@code 'inFileName'}) had (but have been copied to directory 1167 * {@code 'outFileOrDirName'}). 1168 * </DIV> 1169 * 1170 * <BR /><DIV CLASS=JDHintAlt> 1171 * <B STYLE="color: red;">Behavior Note:</B> The behavior of this copy operation is generally / 1172 * mostly the same as the standard {@code UNIX} or {@code MS-DOS} commands {@code 'cp'} and 1173 * {@code 'copy'} (respectively) - <B>differing only in this method's ability to have 1174 * directories created using {@code File.mkdirs()}</B> 1175 * </DIV> 1176 * 1177 * @param createDirsIfNotExist If the target output-file is situated in a directory-path that 1178 * does not exist, this method will throw an exception. However, if this boolean parameter is 1179 * set to {@code TRUE} and the aforementioned situation occurs where the complete-directory 1180 * tree does not exist, then this method will first attempt to create the directories using 1181 * {@code java.io.File.mkdirs().} 1182 * 1183 * @throws SecurityException If boolean parameter {@code 'createDirsIfNotExist'} is 1184 * {@code TRUE} <I>and if</I> the directory named by parameter {@code 'outFileName'} does not 1185 * exist, <I>and if</I> attempting to create such a directory is not permitted by the 1186 * Operating-System, then this exception shall throw. 1187 * 1188 * @throws IOException For any number of fail-causes in reading or writing input stream data. 1189 * An explanation of all causes of such an operation is beyond the scope of this 1190 * method-documentation entry. 1191 * 1192 * @throws FileNotFoundException If the {@code 'inFileName'} is not found, or 1193 * {@code 'outFileOrDirName'} uses a directory path that doesn't exist on the File-System, 1194 * <B>and</B> parameter {@code 'createDirsIfNotExist'} is set to {@code FALSE}. 1195 * 1196 * @throws SameSourceAndTargetException This exception will be thrown if the <CODE>Java Virtual 1197 * Machine</CODE> ascertains that the source and target locations point to the same physical 1198 * disk locations. The classes utilized for this operation are from package 1199 * {@code java.nio.file.*}; 1200 * 1201 * @throws InvalidPathException If the <I>Java Virtual Machine</I> is unable to instantiate an 1202 * instance of {@code java.nio.files.Path} for either the {@code 'inFileName'} parameter or the 1203 * {@code 'outFileOrDirName'}, then this exception will be thrown. 1204 * 1205 * @throws NoSuchFileException If, after instantiating an instance of {@code Path} for either 1206 * the {@code source} or the {@code target} locations, the <I>Java Virtual Machine</I> is 1207 * unable to build an instance of {@code Path} using the method {@code Path.toRealPath()}, then 1208 * this exception will throw. 1209 */ 1210 public static void copyFile 1211 (String inFileName, String outFileOrDirName, boolean createDirsIfNotExist) 1212 throws IOException 1213 { 1214 File f = new File(outFileOrDirName); 1215 1216 if (createDirsIfNotExist) if (! f.exists()) f.mkdirs(); 1217 1218 if (f.isDirectory()) 1219 { 1220 if (! outFileOrDirName.endsWith(File.separator)) 1221 outFileOrDirName = outFileOrDirName + File.separator; 1222 1223 outFileOrDirName = outFileOrDirName + StringParse.fromLastFileSeparatorPos(inFileName); 1224 } 1225 1226 String inPath = Paths.get(inFileName).toRealPath().toString(); 1227 // throws InvalidPathException 1228 // throws NoSuchFileException 1229 1230 try 1231 { 1232 if (Paths.get(outFileOrDirName).toRealPath().toString().equals(inPath)) 1233 1234 throw new SameSourceAndTargetException( 1235 "The Source File Name and the Target Location provided to your copyFile " + 1236 "request operation appear to point to the same physical-disk location:\n" + 1237 inPath 1238 ); 1239 } 1240 1241 catch (NoSuchFileException e) { } 1242 1243 1244 // NOTE: Mostly (but not always) the output file won't exist yet... If it does not, 1245 // then we really don't need to worry about over-writing the origina file. 1246 // 1247 // REMEMBER: The only purpose of the above test is to make sure that the source and 1248 // target are not the same (to avoid clobbering the original file) 1249 1250 FileInputStream fis = new FileInputStream(inFileName); 1251 FileOutputStream fos = new FileOutputStream(outFileOrDirName); 1252 byte[] b = new byte[5000]; 1253 int result = 0; 1254 1255 try 1256 { while ((result = fis.read(b)) != -1) fos.write(b, 0, result); } 1257 1258 finally 1259 { fis.close(); fos.flush(); fos.close(); } 1260 } 1261 1262 /** 1263 * Convenience Method. 1264 * <BR />Invokes: {@code java.io.File.delete()} 1265 */ 1266 public static void deleteFiles(String... fileNames) 1267 { for (String fileName : fileNames) (new File(fileName)).delete(); } 1268 1269 /** 1270 * Convenience Method. 1271 * <BR />Invokes: {@link #copyFile(String, String, boolean)} 1272 * <BR />And-Then: deletes 1273 */ 1274 public static void moveFile 1275 (String inFileName, String outFileName, boolean createDirsIfNotExist) 1276 throws IOException 1277 { 1278 copyFile(inFileName, outFileName, createDirsIfNotExist); 1279 (new File(inFileName)).delete(); 1280 } 1281 1282 /** 1283 * This deletes an entire directory, including any sub-directories. It is like the UNIX 1284 * switch {@code -r} for the command {@code rm}, or the old Microsoft DOS Command 1285 * {@code 'deltree'} for deleting directories. It simply reuses the class {@code FileTransfer} 1286 * 1287 * <BR /><BR /><B CLASS=JDDescLabel>Platform Independance (WORA):</B> 1288 * 1289 * <BR />If this method is invoked from a UNIX or LINUX platform, then it will, generally, 1290 * bring about identical results as a call to {@link Shell#RM(String, String)} where the UNIX 1291 * {@code "-r"} (recursive) flag has been included / set. On such a platform, an entire 1292 * directory would be eliminated. 1293 * 1294 * <BR /><BR />However, if executed from Windows, the class {@link Shell} would fail since it 1295 * only works on UNIX, but this method here would still succeed at its task. It is a 1296 * "Platform-Independent" function. 1297 * 1298 * @param directoryName This should be a valid directory on the File-System. 1299 * 1300 * <BR /><BR /><DIV CLASS=JDHint> 1301 * <B STYLE="color: red">WARNING:</B> This command <B>does indeed delete the entire 1302 * Directory-Tree of the named directory!</B> 1303 * </DIV> 1304 * 1305 * @param reCreateDirectoryOnExit This parameter allows the user to create an <I>an empty 1306 * directory with the same name</I> as the directory that was just deleted, after all of the 1307 * directory's contents have been deleted. When this parameter is passed a value of 1308 * {@code TRUE}, the equivalent of the UNIX command {@code mkdir 'directoryName'} will be 1309 * executed prior to exiting this method. 1310 * 1311 * <BR /><BR />This can be a small convenience if the user desired that the directory be 1312 * cleared, rather than deleted completely. 1313 * 1314 * @param log This parameter may be null, and if it is, it will be ignored. This shall receive 1315 * textual log output from the deletion process. 1316 * 1317 * <EMBED CLASS='external-html' DATA-FILE-ID=APPENDABLE> 1318 * 1319 * @return This shall return a count on the total number of deleted files. Note that when 1320 * directories are deleted (not files), their deletion <I>shall not count towards</I> the 1321 * total returned in this integer. 1322 * 1323 * @throws IllegalArgumentException Throws if the {@code String} provided to parameter 1324 * {@code directoryName} does not name a valid directory on the File-System. 1325 * 1326 * @throws IOException May be thrown by the File-System Methods which underly this method. 1327 */ 1328 public static int delTree 1329 (String directoryName, boolean reCreateDirectoryOnExit, Appendable log) 1330 throws IOException 1331 { 1332 if (directoryName == null) throw new NullPointerException 1333 ("You have provided null to parameter 'directoryName', but this is not allowed here."); 1334 1335 File f = new File(directoryName); 1336 1337 if (! f.exists()) throw new IllegalArgumentException( 1338 "The directory name you have provided: [" + directoryName + "] was not found on the " + 1339 "File System. Aborted." 1340 ); 1341 1342 if (! f.isDirectory()) throw new IllegalArgumentException( 1343 "The value you have provided to parameter 'directoryName' was: " + 1344 "[" + directoryName + "], but unfortunately this is not the name of a directory on " + 1345 "the File System, but rather a file. This is not allowed here." 1346 ); 1347 1348 // Uses class FileNode to build the directory into Java Memory. 1349 // It is possibly of interest to note, that if running this java code on a UNIX or 1350 // LINUX platform, this method should perform the exact same operation as an invocation 1351 // of Shell.RM(directoryName, "-r"); 1352 1353 FileNode fn = FileNode.createRoot(directoryName).loadTree(); 1354 1355 int ret = FileTransfer.deleteFilesRecursive(fn, null, null, log); 1356 1357 if (reCreateDirectoryOnExit) f.mkdirs(); 1358 1359 return ret; 1360 } 1361 1362 /** 1363 * This may read a Text-File containing integer data. If this data is a <B>Comma Separated 1364 * Value</B> {@code 'CSV'} Text-File, please pass {@code TRUE} to the parameter 1365 * {@code 'isCSV'}. If this file contains integers that have commas between digits in groups 1366 * of three (like {@code '30,000'}) please pass {@code TRUE} to the parameter 1367 * {@code 'hasCommasInInts'}. 1368 * 1369 * <EMBED CLASS='external-html' DATA-TYPE=int DATA-FILE-ID=FRW_READNUM_FFORMAT> 1370 * 1371 * <BR /><BR /><DIV CLASS=JDHint> 1372 * <B STYLE='color: red;'>Number-Format:</B> The numbers in this Text-File must be parse-able 1373 * by Java class {@code java.lang.Integer} via its method 1374 * {@code Integer.parseInt(String s, int radix)} 1375 * </DIV> 1376 * 1377 * @param fileName This should contain the File-Name which itself contains a list of integers. 1378 * These integers may be separated by either a comma ({@code ','}) or a space ({@code ' '}). 1379 * 1380 * @param hasCommasInInts It is allowed that the file named by {@code 'fileName'} contain 1381 * integers which use the commonly found notation of having a comma between groups of three 1382 * digits within an integer. For instance the number {@code '98765'}, to a reader, is often 1383 * represented as {@code '98,765'}. When this parameter is set to {@code TRUE}, this method 1384 * shall simply remove any comma that is found juxtaposed between two digits before 1385 * processing any text found in the file. 1386 * 1387 * @param isCSV If the text file named by {@code 'fileName'} is a <B>Comma Separated Value</B> 1388 * file, then please pass {@code TRUE} to this parameter. If {@code FALSE} is passed here, 1389 * then it is mandatory that the individual numbers inside the Text-File are separated by at 1390 * least one white-space character. 1391 * 1392 * <BR /><BR /><DIV CLASS=JDHintAlt> 1393 * <B STYLE='color:red;'>Important:</B> If it is decided to set both of the boolean parameters 1394 * to {@code TRUE} - <I>where the integers have commas, <B STYLE="color: red">and</B> the 1395 * integers are separated by commas</I>, it is up to the programmer to ensure that the 1396 * individual numbers, themselves, are <I>not only</I> separated by a comma, <I>but also</I> 1397 * separated by a space as well. 1398 * </DIV> 1399 * 1400 * @param radix This is the {@code 'radix'}, which is also usually called the number's 1401 * {@code 'base'} that is to be used when parsing the numbers. Since Java's {@code class 1402 * java.lang.Integer} is used to perform the parse, <I>both</I> the {@code 'radix'}, <I>and</I> 1403 * the data found in the Text-File must conform to the Java method 1404 * {@code Integer.parseInt(String s, int radix)}. 1405 * 1406 * <BR /><BR /><DIV CLASS=JDHint> 1407 * This parameter may not be ignored. If the numbers in the Text-File are to be interpreted as 1408 * standard {@code 'decimal'} (<I>Base 10</I>) numbers, then the user should simply pass the 1409 * constant {@code '10'} to this parameter. 1410 * </DIV> 1411 * 1412 * @return This method shall return a {@code java.util.stream.IntStream} consisting of the 1413 * integers that were found within the Text-File provided by {@code 'fileName'}. 1414 * 1415 * <BR /><BR /><DIV CLASS=JDHintAlt> 1416 * An instance of {@code IntStream} is easily converted to an {@code int[]} array using the 1417 * method {@code IntStream.toArray()}. 1418 * </DIV> 1419 * 1420 * @throws FileNotFoundException If the file named by parameter {@code 'fileName'} is not 1421 * found or not accessible in the File-System, then this exception will throw. 1422 * 1423 * @throws IOException This exception throws if there are any errors that occur while 1424 * reading this file from the File-System. 1425 * 1426 * @throws NumberFormatException If any of the numbers read from the Text-File are not 1427 * properly formatted, then this exception shall throw. 1428 * 1429 * @see StringParse#NUMBER_COMMMA_REGEX 1430 * @see StringParse#COMMA_REGEX 1431 * @see StringParse#WHITE_SPACE_REGEX 1432 */ 1433 public static IntStream readIntsFromFile 1434 (String fileName, boolean hasCommasInInts, boolean isCSV, int radix) 1435 throws FileNotFoundException, IOException 1436 { 1437 FileReader fr = new FileReader(fileName); 1438 BufferedReader br = new BufferedReader(fr); 1439 IntStream.Builder b = IntStream.builder(); 1440 String s = ""; 1441 1442 while ((s = br.readLine()) != null) 1443 { 1444 // Skip blank lines. 1445 if ((s = s.trim()).length() == 0) continue; 1446 1447 // This line simply finds String-Matches that match "Digit,Digit" and replaces 1448 // such matches with "DigitDigit". After this replacement, they are parsed with ease. 1449 // NOTE: NUMBER_COMMMA_REGEX = Pattern.compile("\\d,\\d"); 1450 1451 if (hasCommasInInts) 1452 s = StringParse.NUMBER_COMMMA_REGEX.matcher(s).replaceAll("$1$2").trim(); 1453 1454 String[] numbers = isCSV 1455 ? StringParse.COMMA_REGEX.split(s) 1456 : StringParse.WHITE_SPACE_REGEX.split(s); 1457 1458 for (String number : numbers) 1459 1460 if ((number = number.trim()).length() > 0) 1461 b.accept(Integer.parseInt(number, radix)); 1462 } 1463 1464 br.close(); 1465 fr.close(); 1466 return b.build(); 1467 } 1468 1469 /** 1470 * This may read a Text-File containing integer data. If this data is a <B>Comma Separated 1471 * Value</B> {@code 'CSV'} Text-File, please pass {@code TRUE} to the parameter 1472 * {@code 'isCSV'}. If this file contains integers that have commas between digits in groups 1473 * of three (like {@code '30,000'}) pleas pass {@code TRUE} to the parameter 1474 * {@code 'hasCommasInLongs'}. 1475 * 1476 * <EMBED CLASS='external-html' DATA-TYPE=long DATA-FILE-ID=FRW_READNUM_FFORMAT> 1477 * 1478 * <BR /><BR /><DIV CLASS=JDHint> 1479 * <B STYLE='color: red;'>Number-Format:</B> The numbers in this Text-File must be parse-able 1480 * by Java class {@code class java.lang.Long} using the method 1481 * {@code Long.parseLong(String s, int radix)} 1482 * </DIV> 1483 * 1484 * @param fileName This should contain the File-Name which itself contains a list of 1485 * {@code 'long'} integers. These {@code long} integers may be separated by either a comma 1486 * ({@code ','}) or a space ({@code ' '}). 1487 * 1488 * @param hasCommasInLongs It is allowed that the file named by {@code 'fileName'} contain 1489 * {@code long}-integers which use the commonly found notation of having a comma between groups 1490 * of three digits within a {@code long} integer. For instance the number {@code '98765'}, to 1491 * a reader, is often represented as {@code '98,765'}. When this parameter is set to 1492 * {@code TRUE}, this method shall simply remove any comma that is found juxtaposed between 1493 * two digits before processing any text found in the file. 1494 * 1495 * @param isCSV If the text file named by {@code 'fileName'} is a <B>Comma Separated Value</B> 1496 * file, then please pass {@code TRUE} to this parameter. If {@code FALSE} is passed here, 1497 * then it is mandatory that the individual numbers inside the Text-File are separated by at 1498 * least one white-space character. 1499 * 1500 * <BR /><BR /><DIV CLASS=JDHintAlt> 1501 * <B STYLE='color: red'>Important:</B> If it is decided to set both of the boolean parameters 1502 * to {@code TRUE} - <I>where the {@code long} integers have commas, 1503 * <B STYLE='color: red'>and</B> the {@code long} integers are separated by commas</I>, it is 1504 * up to the programmer to ensure that the individual numbers, themselves, are <I>not only</I> 1505 * separated by a comma, <I>but also</I> separated by a space as well. 1506 * </DIV> 1507 * 1508 * @param radix This is the {@code 'radix'}, which is also usually called the number's 1509 * {@code 'base'} that is to be used when parsing the numbers. Since Java's {@code class 1510 * Long} is used to perform the parse, <I>both</I> the {@code 'radix'}, <I>and</I> the data 1511 * found in the Text-File must conform to the Java method 1512 * {@code Long.parseLong(String s, int radix)}. 1513 * 1514 * <BR /><BR /><DIV CLASS=JDHint> 1515 * This parameter may not be ignored. If the numbers in the Text-File are to be interpreted as 1516 * standard {@code 'decimal'} (<I>Base 10</I>) numbers, then the user should simply pass the 1517 * constant {@code '10'} to this parameter. 1518 * </DIV> 1519 * 1520 * @return This method shall return a {@code java.util.stream.LongStream} consisting of the 1521 * {@code long}-integers that were found within the Text-File provided by {@code 'fileName'}. 1522 * 1523 * <BR /><BR /><DIV CLASS=JDHintAlt> 1524 * An instance of {@code LongStream} is easily converted to a {@code long[]} array using the 1525 * method {@code LongStream.toArray()}. 1526 * </DIV> 1527 * 1528 * @throws FileNotFoundException If the file named by parameter {@code 'fileName'} is not 1529 * found or not accessible in the File-System, then this exception will throw. 1530 * 1531 * @throws IOException This exception throws if there are any errors that occur while 1532 * reading this file from the File-System. 1533 * 1534 * @throws NumberFormatException If any of the numbers read from the Text-File are not 1535 * properly formatted, then this exception shall throw. 1536 * 1537 * @see StringParse#NUMBER_COMMMA_REGEX 1538 * @see StringParse#COMMA_REGEX 1539 * @see StringParse#WHITE_SPACE_REGEX 1540 */ 1541 public static LongStream readLongsFromFile 1542 (String fileName, boolean hasCommasInLongs, boolean isCSV, int radix) 1543 throws FileNotFoundException, IOException 1544 { 1545 FileReader fr = new FileReader(fileName); 1546 BufferedReader br = new BufferedReader(fr); 1547 LongStream.Builder b = LongStream.builder(); 1548 String s = ""; 1549 1550 while ((s = br.readLine()) != null) 1551 { 1552 // Skip blank lines. 1553 if ((s = s.trim()).length() == 0) continue; 1554 1555 // This line simply finds String-Matches that match "Digit,Digit" and replaces 1556 // such matches with "DigitDigit". After this replacement, they are parsed with ease. 1557 // NOTE: NUMBER_COMMMA_REGEX = Pattern.compile("\\d,\\d"); 1558 1559 if (hasCommasInLongs) 1560 s = StringParse.NUMBER_COMMMA_REGEX.matcher(s).replaceAll("$1$2").trim(); 1561 1562 String[] numbers = isCSV 1563 ? StringParse.COMMA_REGEX.split(s) 1564 : StringParse.WHITE_SPACE_REGEX.split(s); 1565 1566 for (String number : numbers) 1567 if ((number = number.trim()).length() > 0) 1568 b.accept(Long.parseLong(number, radix)); 1569 } 1570 1571 br.close(); 1572 fr.close(); 1573 return b.build(); 1574 } 1575 1576 /** 1577 * This may read a Text-File containing floating-point data. If this data is a <B>Comma 1578 * Separated Value</B> {@code 'CSV'} Text-File, please pass {@code TRUE} to the parameter 1579 * {@code 'isCSV'}. If this file contains {@code double's} that have commas between digits 1580 * in groups of three (like {@code '30,000,000,00'}) pleas pass {@code TRUE} to the parameter 1581 * {@code 'hasCommasInDoubles'}. 1582 * 1583 * <EMBED CLASS='external-html' DATA-TYPE=long DATA-FILE-ID=FRW_READNUM_FFORMAT> 1584 * 1585 * <BR /><BR /><DIV CLASS=JDHint> 1586 * <B STYLE='color: red;'>Number-Format:</B> The numbers in this Text-File must be parse-able 1587 * by Java class {@code class java.lang.Double} using the method 1588 * {@code Double.parseDouble(String s)} 1589 * </DIV> 1590 * 1591 * @param fileName This should contain the File-Name which itself contains a list of 1592 * {@code 'double'} values. These {@code double's} may be separated by either a comma 1593 * ({@code ','}) or a space ({@code ' '}). 1594 * 1595 * @param hasCommasInDoubles It is allowed that the file named by {@code 'fileName'} contain 1596 * {@code double}-values which use the commonly found notation of having a comma between groups 1597 * of three digits within a {@code double} value. For instance the number {@code '98765.01'}, 1598 * to a reader, can be represented as {@code '98,765.01'}. When this parameter is set to 1599 * {@code TRUE}, this method shall simply remove any comma that is found juxtaposed between 1600 * two digits before processing any text found in the file. 1601 * 1602 * @param isCSV If the text file named by {@code 'fileName'} is a <B>Comma Separated Value</B> 1603 * file, then please pass {@code TRUE} to this parameter. If {@code FALSE} is passed here, 1604 * then it is mandatory that the individual numbers inside the Text-File are separated by at 1605 * least one white-space character. 1606 * 1607 * <BR /><BR /><DIV CLASS=JDHintAlt> 1608 * <B STYLE="color: red">Important:</B> If it is decided to set both of the boolean parameters 1609 * to {@code TRUE} - <I>where the {@code double} values have commas, 1610 * <B STYLE="color: red">and</B> the {@code double} values are separated by commas</I>, it is 1611 * up to the programmer to ensure that the individual numbers, themselves, are <I>not only</I> 1612 * separated by a comma, <I>but also</I> separated by a space as well. 1613 * </DIV> 1614 * 1615 * @return This method shall return a {@code java.util.stream.DoubleStream} consisting of the 1616 * {@code double}-values that were found within the Text-File provided by {@code 'fileName'}. 1617 * 1618 * <BR /><BR /><DIV CLASS=JDHint> 1619 * An instance of {@code DoubleStream} is easily converted to a {@code double[]} array using 1620 * the method {@code DoubleStream.toArray()}. 1621 * </DIV> 1622 * 1623 * @throws FileNotFoundException If the file named by parameter {@code 'fileName'} is not 1624 * found or not accessible in the File-System, then this exception will throw. 1625 * 1626 * @throws IOException This exception throws if there are any errors that occur while 1627 * reading this file from the File-System. 1628 * 1629 * @throws NumberFormatException If any of the numbers read from the Text-File are not 1630 * properly formatted, then this exception shall throw. 1631 * 1632 * @see StringParse#NUMBER_COMMMA_REGEX 1633 * @see StringParse#COMMA_REGEX 1634 * @see StringParse#WHITE_SPACE_REGEX 1635 */ 1636 public static DoubleStream readDoublesFromFile 1637 (String fileName, boolean hasCommasInDoubles, boolean isCSV) 1638 throws FileNotFoundException, IOException 1639 { 1640 FileReader fr = new FileReader(fileName); 1641 BufferedReader br = new BufferedReader(fr); 1642 DoubleStream.Builder b = DoubleStream.builder(); 1643 String s = ""; 1644 1645 while ((s = br.readLine()) != null) 1646 { 1647 // Skip blank lines. 1648 if ((s = s.trim()).length() == 0) continue; 1649 1650 // This line simply finds String-Matches that match "Digit,Digit" and replaces 1651 // such matches with "DigitDigit". After this replacement, they are parsed with ease. 1652 // NOTE: NUMBER_COMMMA_REGEX = Pattern.compile("\\d,\\d"); 1653 1654 if (hasCommasInDoubles) 1655 s = StringParse.NUMBER_COMMMA_REGEX.matcher(s).replaceAll("$1$2").trim(); 1656 1657 String[] numbers = isCSV 1658 ? StringParse.COMMA_REGEX.split(s) 1659 : StringParse.WHITE_SPACE_REGEX.split(s); 1660 1661 for (String number : numbers) 1662 1663 if ((number = number.trim()).length() > 0) 1664 b.accept(Double.parseDouble(number)); 1665 } 1666 1667 br.close(); 1668 fr.close(); 1669 return b.build(); 1670 } 1671 1672 /** 1673 * Convenience Method. 1674 * <BR />Invokes: {@code java.io.FileOutputStream.write(byte[])}. 1675 * <BR /><B>NOTE:</B> This may throw {@code IOException, FileNotFoundException}, etc... 1676 */ 1677 public static void writeBinary(byte[] bArr, String fileName) throws IOException 1678 { 1679 // The input-stream is 'java.lang.AutoCloseable'. 1680 // 1681 // NOTE: The IOException will still be thrown out of this method if it occurs. It is not 1682 // caught! This also (potentially) throws SecurityException & FileNotFoundException 1683 1684 try 1685 (FileOutputStream fos = new FileOutputStream(fileName)) 1686 { fos.write(bArr); } 1687 } 1688 1689 1690 /** 1691 * Convenience Method. 1692 * <BR />Invokes: {@code java.io.FileOutputStream.write(byte[], int, int)}. 1693 * <BR /><B>NOTE:</B> This may throw {@code IOException, IndexOutOfBoundsException}, etc... 1694 */ 1695 public static void writeBinary(byte[] bArr, int offset, int len, String fileName) 1696 throws IOException 1697 { 1698 // The input-stream is 'java.lang.AutoCloseable'. 1699 // 1700 // NOTE: The IOException will still be thrown out of this method if it occurs. It is not 1701 // caught! This also (potentially) throws SecurityException, FileNotFoundException, 1702 // IndexOutOfBoundsExcepton (if 'offset' or 'len' do not adhere to 'bArr' definition) 1703 1704 try 1705 (FileOutputStream fos = new FileOutputStream(fileName)) 1706 { fos.write(bArr, offset, len); } 1707 } 1708 1709 /** 1710 * Convenience Method. 1711 * <BR />Invokes: {@link #readBinary(String, int, int)}. 1712 */ 1713 public static byte[] readBinary(String fileName) 1714 throws FileNotFoundException, IOException 1715 { return readBinary(fileName, 0, -1); } 1716 1717 /** 1718 * Reads data from a binary file into a Java {@code byte[]} array. 1719 * 1720 * <BR /><BR />Unlike Java's {@code FileOutputStream.write(...)} method, the {@code read(...)} 1721 * Java provides in that same exact class is more difficult to use. This method is much longer 1722 * than its corresponding {@link #writeBinary(byte[], String)} method. 1723 * 1724 * @param fileName The name of the file (on the File-System) to read. 1725 * 1726 * @param offset The number of {@code byte's} to skip before appending a {@code byte} into the 1727 * output {@code byte[]} array. If the value provided to parameer {@code 'offset'} is longer 1728 * than the size of the file itself, then a <B>zero-length {@code byte[]} array</B> will be 1729 * returned. 1730 * 1731 * <BR /><BR /><DIV CLASS=JDHint> 1732 * The meaning of the value in parameter {@code 'offset'} is very different from the meaning of 1733 * a parameter by that exact same name, except in method {@code read(...)} of class 1734 * {@code FileInputStream}. <B>HERE</B> the offset is the number of <I>bytes to skip inside of 1735 * file {@code 'fileName'}, before saving the values that are read froom disk.</I> In the 1736 * {@code FileInputStream}, offset is used as an array pointer. 1737 * </DIV> 1738 * 1739 * @param len Once the internal-loop has begun copying bytes from the Data-File into the 1740 * returned {@code byte[]} array, {@code byte's} will continue to be copied into this array 1741 * until precisely {@code 'len'} bytes have been copied. 1742 * 1743 * <BR /><BR /><DIV CLASS=JDHintAlt> 1744 * The user may provide any negative number to this parameter, and the read process will simply 1745 * begin at position {@code 'offset'}, and continue reading until the End of the File has been 1746 * reached. 1747 * </DIV> 1748 * 1749 * @return Returns a {@code byte[]}-array, with a length of (parameter) {@code 'len'} bytes. 1750 * 1751 * @throws FileNotFoundException Class {@code java.io.FileInputStream} will throw a 1752 * {@code FileNotFoundException} if that class is passed a {@code 'fileName'} that does not 1753 * exist, or is a File-Name that represents a directory not a file. 1754 * 1755 * @throws SecurityException If a security manager exists and its {@code checkRead} method 1756 * denies read access to the file. 1757 * 1758 * @throws IOException The {@code FileInputStream} instance, and the {@code java.io.File} 1759 * instance are both capable of throwing {@code IOException}. 1760 * 1761 * @throws IllegalArgumentException 1762 * 1763 * <BR /><BR /><UL CLASS=JDUL> 1764 * <LI> The value provided to {@code 'offset'} is negative</LI> 1765 * <LI> The value provided to parameter {@code 'len'} is zero.</LI> 1766 * </UL> 1767 * 1768 * @throws FileSizeException This exception throws if any of the following are detected: 1769 * 1770 * <BR /><BR /><UL CLASS=JDUL> 1771 * 1772 * <LI> The returned {@code byte[]} array cannot have a size larger than 1773 * {@code Integer.MAX_VALUE}. If the returned array would have a larger size - <I>based 1774 * on any combination of provided input values</I>, then this exception will throw. 1775 * <BR /><BR /> 1776 * </LI> 1777 * 1778 * <LI> The value provided to {@code 'offset'} is larger than the size of the underlying 1779 * file that is specified by parameter {@code 'fileName'} 1780 * <BR /><BR /> 1781 * </LI> 1782 * 1783 * <LI> The total of {@code offset + len} is larger than the size of the underlying file. 1784 * <BR /><BR /> 1785 * </LI> 1786 * 1787 * <LI> An invocation of the method {@code File.length()} returns zero, indicating that the 1788 * file is either unreadable, non-existant, or possibly empty. 1789 * </LI> 1790 * 1791 * </UL> 1792 * 1793 * @throws EOFException This particular exception should not be expected. Before any reads are 1794 * done, the size of the Data-File is first checked to see if it is big enough to have the 1795 * amount of data that is requested by input paramters {@code 'offset'} and {@code 'len'}. If 1796 * however, an error occurrs, and the Operating System returns an {@code EOF} earlier than 1797 * expected (for unforseen reasons), then {@code EOFException} would throw. 1798 */ 1799 public static byte[] readBinary(String fileName, int offset, int len) 1800 throws FileNotFoundException, IOException 1801 { 1802 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1803 // EXCEPTION CHECKS 1804 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1805 1806 if (offset < 0) throw new IllegalArgumentException 1807 ("The value of offset (" + offset + ") is negative."); 1808 1809 if (len == 0) throw new IllegalArgumentException 1810 ("A value of zero was provided to parameter 'len.' This is not allowed"); 1811 1812 File f = new File(fileName); 1813 1814 try 1815 (FileInputStream fis = new FileInputStream(f)) 1816 { 1817 long fileLen = f.length(); 1818 1819 // A file-name that points to a completely empty file was passed to parameter 'fileName' 1820 // **OR** the operating system returned 0 for some other error-related reason. 1821 // According to the Java-Doc explanation, a '0' returned value might mean there were 1822 // errors when trying to access the file. 1823 1824 if (fileLen == 0) throw new FileSizeException( 1825 "Calling java.io.File.length() returned 0 for the file-name that was " + 1826 "provided to this method:\n[" + fileName + "]. This might mean that the file " + 1827 "does not exist, or has other issues.", 1828 0 1829 ); 1830 1831 // The file would end before we have even started reading bytes - after having skipped 1832 // 'offset' nmber of initial bytes. 1833 1834 if (offset > fileLen) throw new FileSizeException( 1835 "The value of offset (" + offset + ") is larger than the size of the binary " + 1836 "file, which only had size (" + fileLen + ").", 1837 fileLen 1838 ); 1839 1840 // If 'len' was passed a negative value, that value is actually meaningless - and was 1841 // just used to indicate that reading the entire file starting at byte # 'offset' is 1842 // what the user is requesting. 1843 1844 if (len > 0) 1845 1846 // This simply checks how many bytes the file would need to have to provide for 1847 // reading from 'offset' up until 'offset + len' 1848 1849 if ((offset + len) > fileLen) throw new FileSizeException( 1850 "The file [" + fileName + "] apparently has a size of [" + fileLen + "], " + 1851 "but the offset (" + offset + ") and the length (" + len + ") that was " + 1852 "requested sum to (" + (offset+len) + "), which is greater than that size.", 1853 fileLen 1854 ); 1855 1856 1857 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1858 // More Exception Checks: Check what the size of the returned byte[] array will be 1859 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1860 1861 // Negative-Length means that bytes are read until an EOF occurrs 1862 if (len < 0) 1863 { 1864 long bytesToRead = fileLen - offset; 1865 1866 if (bytesToRead > Integer.MAX_VALUE) throw new FileSizeException( 1867 "This file has [" + fileLen + "] bytes of data, and even with an offset of " + 1868 '[' + offset + "], there are still " + bytesToRead + " bytes of data to " + 1869 "place into the array. This value is larger than Integer.MAX_VALUE " + 1870 "(" + Integer.MAX_VALUE + ").", 1871 fileLen 1872 ); 1873 1874 else len = (int) bytesToRead; 1875 } 1876 1877 // A Positive length means that exactly the value inside input-parameter 'len' need to 1878 // be placed into the returned byte[] array. 1879 1880 else if (len > Integer.MAX_VALUE) throw new FileSizeException( 1881 "This file has [" + fileLen + "] bytes of data, which is larger than " + 1882 "Integer.MAX_VALUE (" + Integer.MAX_VALUE + ").", 1883 fileLen 1884 ); 1885 1886 1887 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1888 // FIRST, if there is a NON-ZERO OFFSET, then exactly OFFSET bytes must be skipped 1889 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1890 1891 int failedReads = 0; 1892 long bytesSkipped = 0; 1893 int totalBytesSkipped = 0; 1894 int bytesRemaining = offset; 1895 1896 // NOTE: This loop is skipped, immediately / automatically, if indeed 'offset' is zero 1897 while (totalBytesSkipped < offset) 1898 1899 if ((bytesSkipped = fis.skip(bytesRemaining)) == 0) 1900 { 1901 if (failedReads++ == 10) throw new IOException( 1902 "There have 10 repeated attempts to read the file's data, but all " + 1903 "attempts have resulted in empty-reads with zero bytes being retrieved " + 1904 "from disk." 1905 ); 1906 } 1907 else 1908 { 1909 // I don't see why this should happen, but it will be left here, just in 1910 // case Java screws up. 1911 1912 if (bytesSkipped > bytesRemaining) throw new InternalError( 1913 "This error is being thrown because the Java Virtual Machine has " + 1914 "skipped past the end of the requested offset. This has occured while " + 1915 "calling FileInputStream.skip(offset)." 1916 ); 1917 1918 // NOTE: I am *FULLY AWARE* this is redundant, but the variable names are 1919 // the only thing that is very readable about this method. 1920 1921 totalBytesSkipped += bytesSkipped; 1922 bytesRemaining -= bytesSkipped; 1923 } 1924 1925 1926 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1927 // THEN, Read the bytes from the file 1928 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1929 1930 byte[] retArr = new byte[len]; 1931 int arrPos = 0; 1932 1933 // This one was already defined / declared in the previous part. Initialize it, but 1934 // don't re-declare it. 1935 bytesRemaining = len; 1936 1937 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1938 // This loop exits when all (requested) bytes have been read, or EOFException! 1939 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1940 1941 while (bytesRemaining > 0) 1942 { 1943 int bytesRead = fis.read(retArr, arrPos, bytesRemaining); 1944 1945 1946 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1947 // Case 1: The EOF Marker was reached, before filling up the response-array 1948 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1949 1950 if (bytesRead == -1) if (bytesRemaining > 0) throw new EOFException( 1951 "The end of file '" + fileName + "' was reached before " + len + 1952 " bytes were read. Only " + arrPos + " bytes were read." 1953 ); 1954 1955 1956 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1957 // Yes, this seems redundant, but it's just the way it is 1958 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1959 // 1960 // arrPos ==> 0 ... retArr.length (retArr.length => input-param 'len') 1961 // bytesRemaining ==> 'len' ... 0 1962 1963 arrPos += bytesRead; 1964 bytesRemaining -= bytesRead; 1965 1966 1967 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1968 // Case 2: Exactly the appropriate number of bytes were read. 1969 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1970 1971 if (arrPos == retArr.length) return retArr; 1972 1973 1974 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1975 // Case 3: This should not be reachable! 1976 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1977 // 1978 // Java's FileInputStream.read method is supposed to stop when it has read 1979 // 'bytesRemaining' number of bytes! 1980 1981 if (arrPos > retArr.length) throw new UnreachableError(); 1982 } 1983 1984 // One of the cases inside the loop body must fire before the loop ever actually exits, 1985 // unless I'm missing something! 1986 throw new UnreachableError(); 1987 } 1988 } 1989 1990 /** 1991 * Attempts to read a {@code java.lang.Class} from a class-file. 1992 * 1993 * <BR /><BR />It is not neccessarily always knownst to the programmer what the 1994 * <I>Full-Package Class-Name</I> of the {@code class} that's inside the class-file actually 1995 * is. 1996 * 1997 * @param classFileName The name of any class-file on the File-System. 1998 * 1999 * @param possibleClassNames If the exact <B STYLE='color: red;'>Full-Package Name</B> of the 2000 * class being read is known, then that ought to be the only {@code String} passed to this 2001 * Var-Args {@code String}-Parameter. If there are multiple possibilities, pass all of them, 2002 * and all we be used in an attempt to parse & load this class. 2003 * 2004 * <BR /><BR /><B>DEVELOPER NOTE:</B> Perhaps I am stupid, but I cannot find any way (using the 2005 * standard JDK) to read a class file, and then ask that class-file either: 2006 * 2007 * <BR /><BR /><UL CLASS=JDUL> 2008 * <LI>What the name of the {@code Class} in the Class-File is?</LI> 2009 * <LI>What the name of the {@code Package} being used in that Class-File is?</LI> 2010 * </UL> 2011 * 2012 * <BR />So, for now, a Var-Args {@code String}-Array is required. 2013 * 2014 * <BR /><BR />To add insult to injury, the standard Java Exception class 2015 * {@code 'TypeNotPresentException'} doesn't have a constructor that accepts a 2016 * {@code 'message'} parameter (it accepts a {@code 'typeName'}) parameter instead. As a 2017 * result, if this method throws that exception, the error-message printed has a few 2018 * 'extranneous characters' <B>BOTH</B> before the actual message, <B>AND</B> after it. It is 2019 * staying this way because Java's Description of that exception matches precisely with its 2020 * use here. 2021 * 2022 * @return An instance of {@code java.lang.Class} that was contained by the Class-File. 2023 * 2024 * @throws IOException Java may throw several exceptions while attempting to load 2025 * the {@code '.class'} file into a {@code byte[]} array. Such exceptions may include 2026 * {@code IOException}, {@code SecurityException}, {@code FileNotFoundException} etc... 2027 * 2028 * @throws TypeNotPresentException If none of the names listed in Var-Args {@code String[]} 2029 * Array parameter {@code 'possibleClassNames'} are consistent with 2030 * <B STYLE='color:red'><I>BOTH</I></B> the package-name <B STYLE='color:red;'>AND</B> the 2031 * class-name of the actual class that resides inside {@code 'classFileName'}, then this 2032 * exception will throw. 2033 * 2034 * <BR /><BR /><DIV CLASS=JDHint> 2035 * <B STYLE='color:red'>Note:</B> Rather than simply returning 'null', this 2036 * {@code RuntimeException} is thrown as a nicely worded error message is provided. 2037 * </DIV> 2038 */ 2039 public static Class<?> readClass(String classFileName, String... possibleClassNames) 2040 throws IOException 2041 { 2042 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 2043 // Load the entire '.class' file into a byte[] array. 2044 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 2045 2046 // Now read the bytes directly into a byte[] array, courtesy of Torello.Java.FileRW 2047 byte[] byteArray = readBinary(classFileName); 2048 2049 2050 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 2051 // Create a ClassLoader - and convert "byte[]" into "Class<?> summarySorterClass" 2052 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 2053 2054 // NOTE: The commented lines below will not work if there is a "Package Name" given to the 2055 // class (in the '.java' file). It is better to just read the raw class-file bytes 2056 // into a Java byte[] array. SPECIFICALLY, if class is not inside of a directory 2057 // that is PRECISELY-CONSISTENT with the full-path package+class name, then the class 2058 // loader listed below will FAIL. (NOTE: If the package+class name is consisten, 2059 // then a programmer wouldn't need ANY OF THIS, and could simply call the normal 2060 // "Class.forName(fullPackageClassName)" to get the class!) 2061 // 2062 // ClassLoader cl = new URLClassLoader(new URL[] { new URL("file://" + path) }); 2063 // Class<?> ret = cl.loadClass(className); 2064 // 2065 // HOWEVER: Creating a new ClassLoader instance that accepts the byte-array (thereby 2066 // exporting the 'protected' method defineClass) *DOES* work. 2067 2068 class ByteClassLoader extends ClassLoader 2069 { 2070 public Class<?> defineClass(String name, byte[] classBytes) 2071 { return defineClass(name, classBytes, 0, classBytes.length); } 2072 } 2073 2074 ByteClassLoader cl = new ByteClassLoader(); 2075 Class<?> ret = null; 2076 StringBuilder sb = new StringBuilder(); 2077 2078 2079 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 2080 // Make attempts to convert the byte[] array into a java.lang.Class 2081 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 2082 2083 for (String fullClassName : possibleClassNames) 2084 2085 try 2086 { return cl.defineClass(fullClassName, byteArray); } 2087 2088 catch (NoClassDefFoundError e) 2089 { 2090 String errorMessage = e.getMessage(); 2091 2092 if (errorMessage != null) sb.append(errorMessage + '\n'); 2093 } 2094 2095 throw new TypeNotPresentException( 2096 "The Class-File: [" + classFileName + "] seems to have been successfully read, but " + 2097 "none of the user provided Canonical-Class Names (including a package) were " + 2098 "consistent with the actual name of the Class/Type inside that class file. Below " + 2099 "are listed the exception-messages received, which include both the actual/complete " + 2100 "canonical class-name, along with your user-provided guesses. Errors Saved:\n" + 2101 StrIndent.indent(sb.toString(), 4), 2102 null 2103 ); 2104 } 2105}