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.StaticFunctional; 013import Torello.JavaDoc.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 public static void writeObjectToTextFile(Object o, String fileName) 989 throws IOException 990 { FileRW.writeFile(StringParse.objToB64MimeStr(o), fileName); } 991 992 /** 993 * This will read a java serialized, and MIME-Converted, MIME-Safe {@code java.lang.String} 994 * from a text file and return the {@code Object} that it represented 995 * 996 * <EMBED CLASS='external-html' DATA-FILE-ID=FRW_1OBJ_FILE> 997 * 998 * @param fileName The name of the file containing the MIME-Encoded Serialized 999 * {@code java.lang.Object}. 1000 * 1001 * @return The {@code Object} that had been encoded into the Text-File. 1002 */ 1003 public static Object readObjectFromTextFile(String fileName) throws IOException 1004 { return StringParse.b64MimeStrToObj(loadFileToString(fileName)); } 1005 1006 /** 1007 * Convenience Method. 1008 * <BR />Invokes: {@link #readObjectFromTextFile(String, Class)} 1009 * <BR />Catches Exception 1010 */ 1011 public static <T> T readObjectFromTextFileNOCNFE(String fileName, Class<T> c) 1012 throws IOException 1013 { 1014 try 1015 { return readObjectFromTextFile(fileName, c); } 1016 1017 catch (ClassNotFoundException cnfe) { return null; } 1018 } 1019 1020 /** 1021 * This will read a java serialized, and MIME-Converted, MIME-Safe {@code java.lang.String} 1022 * from a text file and return the {@code Object} that it represented 1023 * 1024 * <EMBED CLASS='external-html' DATA-FILE-ID=FRW_1OBJ_FILE> 1025 * <EMBED CLASS='external-html' DATA-FILE-ID=FRW_EXAMPLE_01> 1026 * 1027 * @param fileName The name of the file containing the MIME-Encoded Serialized 1028 * {@code java.lang.Object}. 1029 * 1030 * @param c This is the type of the {@code Object} expecting to be read from disk. A value 1031 * for this parameter can always be obtained by referencing the {@code static} field 1032 * {@code '.class'}, which is attached to <I>every {@code Object}</I> in java. 1033 * 1034 * <EMBED CLASS='external-html' DATA-FILE-ID=FRW_CLASS_T> 1035 * 1036 * @param <T> This Type Parameter informs this method what type of Base-64 Serialized Object 1037 * is saved within Text File {@code 'fileName'}. The returned result of this method will have 1038 * this type, for convenience to avoid casting the {@code Object} (<I>unless that 1039 * {@code Object} is a Java Generic, and you wish to avoid a "Raw Types" warning</I>. See 1040 * further details inside the explanation about parameter {@code 'c'}). 1041 * 1042 * @return The {@code Object} that had been encoded into the Text-File. 1043 * 1044 * @throws IOException If an I/O error has occurred as a result of the File-System or disk 1045 * operation. 1046 * 1047 * @throws ClassNotFoundException This exception is thrown when the Java Virtual 1048 * Machine (JVM) tries to load a particular class and the specified class cannot be found in 1049 * the {@code CLASSPATH}. 1050 */ 1051 public static <T> T readObjectFromTextFile(String fileName, Class<T> c) 1052 throws IOException, ClassNotFoundException 1053 { 1054 Object o = StringParse.b64MimeStrToObj(loadFileToString(fileName)); 1055 1056 if (o == null) return null; 1057 if (c.isInstance(o)) return c.cast(o); 1058 1059 throw new ClassNotFoundException( 1060 "Although an object was indeed read from the file you have named [" + fileName + "], " + 1061 "that object was not an instance of [" + c.getName() + "], " + 1062 "but rather of [" + o.getClass().getName() + "]" 1063 ); 1064 } 1065 1066 1067 // ******************************************************************************************** 1068 // ******************************************************************************************** 1069 // Object Input & Object Output Streams 1070 // ******************************************************************************************** 1071 // ******************************************************************************************** 1072 1073 1074 /** 1075 * Creates a simple {@code ObjectInputStream} - usually if multiple {@code Object's} have been 1076 * written to a single file. It was better practice to put {@code Object's} in a 1077 * {@code java.util.Vector}, and write one {@code java.util.Vector} during serialization. 1078 * 1079 * <BR /><BR />This, eventually, can became inadequate when downloading large numbers of HTML 1080 * results, where the need to write a large Data-File (intermittently - by saving intermediate 1081 * results) is needed. 1082 * 1083 * @param fName This is the File-Name of the Data-File where the serialized {@code Object's} 1084 * have been stored. 1085 * 1086 * @param ZIP If this is set to {@code TRUE}, the data will be de-compressed. 1087 * 1088 * @return A java {@code ObjectInputStream} 1089 * 1090 * @throws IOException If an I/O error has occurred as a result of the File-System 1091 * or disk operation. 1092 */ 1093 public static ObjectInputStream getOIS(String fName, boolean ZIP) throws IOException 1094 { 1095 return ZIP 1096 ? new ObjectInputStream(new GZIPInputStream(new FileInputStream(new File(fName)))) 1097 : new ObjectInputStream(new FileInputStream(new File(fName))); 1098 } 1099 1100 /** 1101 * Creates a simple {@code ObjectOutputStream} - usually if multiple {@code Object's} need 1102 * to be written to a single file. It was better practice to put {@code Object's} in a 1103 * {@code java.util.Vector}, and write one {@code java.util.Vector} during serialization. 1104 * 1105 * @param fName This is the File-Name of the Data-File where the serialized {@code Object's} 1106 * will be stored. 1107 * 1108 * @param ZIP If this is set to {@code TRUE}, the data will be compressed. 1109 * 1110 * @return A java {@code ObjectInputStream} 1111 * 1112 * @throws IOException If an I/O error has occurred as a result of the File-System or 1113 * disk operation. 1114 */ 1115 public static ObjectOutputStream getOOS(String fName, boolean ZIP) throws IOException 1116 { 1117 return ZIP 1118 ? new ObjectOutputStream(new GZIPOutputStream(new FileOutputStream(new File(fName)))) 1119 : new ObjectOutputStream(new FileOutputStream(new File(fName))); 1120 } 1121 1122 1123 // ******************************************************************************************** 1124 // ******************************************************************************************** 1125 // Copy, Move, Delete 1126 // ******************************************************************************************** 1127 // ******************************************************************************************** 1128 1129 1130 /** 1131 * This method will perform a byte-for-byte copy of a file from one location to another. 1132 * 1133 * @param inFileName The name of the input-file. It will be byte-for-byte copied to an output 1134 * File-Name. 1135 * 1136 * @param outFileOrDirName The name of the output-file, or the name of the directory where the 1137 * file shall be moved. 1138 * 1139 * <BR /><BR />If this file already exists <I>and it is a file (not a directory)</I> 1140 * it will be over-written. 1141 * 1142 * <BR /><BR />If parameter {@code 'outFileOrDirName'} names a directory - <I>due 1143 * to having an ending {@code File.separator}</I>, <B>but</B> does not appear to name a 1144 * directory that exists, this method will throw a {@code FileNotFoundException}. 1145 * 1146 * <BR /><BR />This can be avoided, however, by passing {@code TRUE} to parameter 1147 * {@code 'createDirsIfNotExist'}. If {@code 'outFileOrDirName'} specifies a directory (by 1148 * virtue of the fact that it ends with the {@code File.separator String}) - <I>and 1149 * {@code 'createDirsIfNotExist'} is {@code TRUE}</I>, then this method will first create any 1150 * and all sub-directories needed using the standard Java's {@code File.mkdirs()} method before 1151 * copying the file. 1152 * 1153 * <BR /><BR /><DIV CLASS=JDHint> 1154 * <B><SPAN STYLE="color: red;">Summary:</SPAN></B> The programmer may provide either a 1155 * Directory-Name or a File-Name to parameter {@code 'outFileOrDirName'}. If a Directory-Name 1156 * was provided, the moved file will <B>keep its name</B> - having the same name as the 1157 * original file, ({@code 'inFileName'}) had (but have been copied to directory 1158 * {@code 'outFileOrDirName'}). 1159 * </DIV> 1160 * 1161 * <BR /><BR /><DIV CLASS=JDHintAlt> 1162 * <B STYLE="color: red;">Behavior Note:</B> The behavior of this copy operation is generally / 1163 * mostly the same as the standard {@code UNIX} or {@code MS-DOS} commands {@code 'cp'} and 1164 * {@code 'copy'} (respectively) - <B>differing only in this method's ability to have 1165 * directories created using {@code File.mkdirs()}</B> 1166 * </DIV> 1167 * 1168 * @param createDirsIfNotExist If the target output-file is situated in a directory-path that 1169 * does not exist, this method will throw an exception. However, if this boolean parameter is 1170 * set to {@code TRUE} and the aforementioned situation occurs where the complete-directory 1171 * tree does not exist, then this method will first attempt to create the directories using 1172 * {@code java.io.File.mkdirs().} 1173 * 1174 * @throws SecurityException If boolean parameter {@code 'createDirsIfNotExist'} is 1175 * {@code TRUE} <I>and if</I> the directory named by parameter {@code 'outFileName'} does not 1176 * exist, <I>and if</I> attempting to create such a directory is not permitted by the 1177 * Operating-System, then this exception shall throw. 1178 * 1179 * @throws IOException For any number of fail-causes in reading or writing input stream data. 1180 * An explanation of all causes of such an operation is beyond the scope of this 1181 * method-documentation entry. 1182 * 1183 * @throws FileNotFoundException If the {@code 'inFileName'} is not found, or 1184 * {@code 'outFileOrDirName'} uses a directory path that doesn't exist on the File-System, 1185 * <B>and</B> parameter {@code 'createDirsIfNotExist'} is set to {@code FALSE}. 1186 * 1187 * @throws SameSourceAndTargetException This exception will be thrown if the <CODE>Java Virtual 1188 * Machine</CODE> ascertains that the source and target locations point to the same physical 1189 * disk locations. The classes utilized for this operation are from package 1190 * {@code java.nio.file.*}; 1191 * 1192 * @throws InvalidPathException If the <I>Java Virtual Machine</I> is unable to instantiate an 1193 * instance of {@code java.nio.files.Path} for either the {@code 'inFileName'} parameter or the 1194 * {@code 'outFileOrDirName'}, then this exception will be thrown. 1195 * 1196 * @throws NoSuchFileException If, after instantiating an instance of {@code Path} for either 1197 * the {@code source} or the {@code target} locations, the <I>Java Virtual Machine</I> is 1198 * unable to build an instance of {@code Path} using the method {@code Path.toRealPath()}, then 1199 * this exception will throw. 1200 */ 1201 public static void copyFile 1202 (String inFileName, String outFileOrDirName, boolean createDirsIfNotExist) 1203 throws IOException 1204 { 1205 File f = new File(outFileOrDirName); 1206 1207 if (createDirsIfNotExist) if (! f.exists()) f.mkdirs(); 1208 1209 if (f.isDirectory()) 1210 { 1211 if (! outFileOrDirName.endsWith(File.separator)) 1212 outFileOrDirName = outFileOrDirName + File.separator; 1213 1214 outFileOrDirName = outFileOrDirName + StringParse.fromLastFileSeparatorPos(inFileName); 1215 } 1216 1217 String inPath = Paths.get(inFileName).toRealPath().toString(); 1218 // throws InvalidPathException 1219 // throws NoSuchFileException 1220 1221 try 1222 { 1223 if (Paths.get(outFileOrDirName).toRealPath().toString().equals(inPath)) 1224 1225 throw new SameSourceAndTargetException( 1226 "The Source File Name and the Target Location provided to your copyFile " + 1227 "request operation appear to point to the same physical-disk location:\n" + 1228 inPath 1229 ); 1230 } 1231 1232 catch (NoSuchFileException e) { } 1233 1234 1235 // NOTE: Mostly (but not always) the output file won't exist yet... If it does not, 1236 // then we really don't need to worry about over-writing the origina file. 1237 // 1238 // REMEMBER: The only purpose of the above test is to make sure that the source and 1239 // target are not the same (to avoid clobbering the original file) 1240 1241 FileInputStream fis = new FileInputStream(inFileName); 1242 FileOutputStream fos = new FileOutputStream(outFileOrDirName); 1243 byte[] b = new byte[5000]; 1244 int result = 0; 1245 1246 try 1247 { while ((result = fis.read(b)) != -1) fos.write(b, 0, result); } 1248 1249 finally 1250 { fis.close(); fos.flush(); fos.close(); } 1251 } 1252 1253 /** 1254 * Convenience Method. 1255 * <BR />Invokes: {@code java.io.File.delete()} 1256 */ 1257 public static void deleteFiles(String... fileNames) 1258 { for (String fileName : fileNames) (new File(fileName)).delete(); } 1259 1260 /** 1261 * Convenience Method. 1262 * <BR />Invokes: {@link #copyFile(String, String, boolean)} 1263 * <BR />And-Then: deletes 1264 */ 1265 public static void moveFile 1266 (String inFileName, String outFileName, boolean createDirsIfNotExist) 1267 throws IOException 1268 { 1269 copyFile(inFileName, outFileName, createDirsIfNotExist); 1270 (new File(inFileName)).delete(); 1271 } 1272 1273 /** 1274 * This deletes an entire directory, including any sub-directories. It is like the UNIX 1275 * switch {@code -r} for the command {@code rm}, or the old Microsoft DOS Command 1276 * {@code 'deltree'} for deleting directories. It simply reuses the class {@code FileTransfer} 1277 * 1278 * <BR /><BR /><B CLASS=JDDescLabel>Platform Independance (WORA):</B> 1279 * 1280 * <BR />If this method is invoked from a UNIX or LINUX platform, then it will, generally, 1281 * bring about identical results as a call to {@link Shell#RM(String, String)} where the UNIX 1282 * {@code "-r"} (recursive) flag has been included / set. On such a platform, an entire 1283 * directory would be eliminated. 1284 * 1285 * <BR /><BR />However, if executed from Windows, the class {@link Shell} would fail since it 1286 * only works on UNIX, but this method here would still succeed at its task. It is a 1287 * "Platform-Independent" function. 1288 * 1289 * @param directoryName This should be a valid directory on the File-System. 1290 * 1291 * <BR /><BR /><DIV CLASS=JDHint> 1292 * <B STYLE="color: red">WARNING:</B> This command <B>does indeed delete the entire 1293 * Directory-Tree of the named directory!</B> 1294 * </DIV> 1295 * 1296 * @param reCreateDirectoryOnExit This parameter allows the user to create an <I>an empty 1297 * directory with the same name</I> as the directory that was just deleted, after all of the 1298 * directory's contents have been deleted. When this parameter is passed a value of 1299 * {@code TRUE}, the equivalent of the UNIX command {@code mkdir 'directoryName'} will be 1300 * executed prior to exiting this method. 1301 * 1302 * <BR /><BR />This can be a small convenience if the user desired that the directory be 1303 * cleared, rather than deleted completely. 1304 * 1305 * @param log This parameter may be null, and if it is, it will be ignored. This shall receive 1306 * textual log output from the deletion process. 1307 * 1308 * <EMBED CLASS='external-html' DATA-FILE-ID=APPENDABLE> 1309 * 1310 * @return This shall return a count on the total number of deleted files. Note that when 1311 * directories are deleted (not files), their deletion <I>shall not count towards</I> the 1312 * total returned in this integer. 1313 * 1314 * @throws IllegalArgumentException Throws if the {@code String} provided to parameter 1315 * {@code directoryName} does not name a valid directory on the File-System. 1316 * 1317 * @throws IOException May be thrown by the File-System Methods which underly this method. 1318 */ 1319 public static int delTree 1320 (String directoryName, boolean reCreateDirectoryOnExit, Appendable log) 1321 throws IOException 1322 { 1323 if (directoryName == null) throw new NullPointerException 1324 ("You have provided null to parameter 'directoryName', but this is not allowed here."); 1325 1326 File f = new File(directoryName); 1327 1328 if (! f.exists()) throw new IllegalArgumentException( 1329 "The directory name you have provided: [" + directoryName + "] was not found on the " + 1330 "File System. Aborted." 1331 ); 1332 1333 if (! f.isDirectory()) throw new IllegalArgumentException( 1334 "The value you have provided to parameter 'directoryName' was: " + 1335 "[" + directoryName + "], but unfortunately this is not the name of a directory on " + 1336 "the File System, but rather a file. This is not allowed here." 1337 ); 1338 1339 // Uses class FileNode to build the directory into Java Memory. 1340 // It is possibly of interest to note, that if running this java code on a UNIX or 1341 // LINUX platform, this method should perform the exact same operation as an invocation 1342 // of Shell.RM(directoryName, "-r"); 1343 1344 FileNode fn = FileNode.createRoot(directoryName).loadTree(); 1345 1346 int ret = FileTransfer.deleteFilesRecursive(fn, null, null, log); 1347 1348 if (reCreateDirectoryOnExit) f.mkdirs(); 1349 1350 return ret; 1351 } 1352 1353 /** 1354 * This may read a Text-File containing integer data. If this data is a <B>Comma Separated 1355 * Value</B> {@code 'CSV'} Text-File, please pass {@code TRUE} to the parameter 1356 * {@code 'isCSV'}. If this file contains integers that have commas between digits in groups 1357 * of three (like {@code '30,000'}) please pass {@code TRUE} to the parameter 1358 * {@code 'hasCommasInInts'}. 1359 * 1360 * <EMBED CLASS='external-html' DATA-TYPE=int DATA-FILE-ID=FRW_READNUM_FFORMAT> 1361 * 1362 * <BR /><BR /><DIV CLASS=JDHint> 1363 * <B STYLE='color: red;'>Number-Format:</B> The numbers in this Text-File must be parse-able 1364 * by Java class {@code java.lang.Integer} via its method 1365 * {@code Integer.parseInt(String s, int radix)} 1366 * </DIV> 1367 * 1368 * @param fileName This should contain the File-Name which itself contains a list of integers. 1369 * These integers may be separated by either a comma ({@code ','}) or a space ({@code ' '}). 1370 * 1371 * @param hasCommasInInts It is allowed that the file named by {@code 'fileName'} contain 1372 * integers which use the commonly found notation of having a comma between groups of three 1373 * digits within an integer. For instance the number {@code '98765'}, to a reader, is often 1374 * represented as {@code '98,765'}. When this parameter is set to {@code TRUE}, this method 1375 * shall simply remove any comma that is found juxtaposed between two digits before 1376 * processing any text found in the file. 1377 * 1378 * @param isCSV If the text file named by {@code 'fileName'} is a <B>Comma Separated Value</B> 1379 * file, then please pass {@code TRUE} to this parameter. If {@code FALSE} is passed here, 1380 * then it is mandatory that the individual numbers inside the Text-File are separated by at 1381 * least one white-space character. 1382 * 1383 * <BR /><BR /><DIV CLASS=JDHintAlt> 1384 * <B STYLE='color:red;'>Important:</B> If it is decided to set both of the boolean parameters 1385 * to {@code TRUE} - <I>where the integers have commas, <B STYLE="color: red">and</B> the 1386 * integers are separated by commas</I>, it is up to the programmer to ensure that the 1387 * individual numbers, themselves, are <I>not only</I> separated by a comma, <I>but also</I> 1388 * separated by a space as well. 1389 * </DIV> 1390 * 1391 * @param radix This is the {@code 'radix'}, which is also usually called the number's 1392 * {@code 'base'} that is to be used when parsing the numbers. Since Java's {@code class 1393 * java.lang.Integer} is used to perform the parse, <I>both</I> the {@code 'radix'}, <I>and</I> 1394 * the data found in the Text-File must conform to the Java method 1395 * {@code Integer.parseInt(String s, int radix)}. 1396 * 1397 * <BR /><BR /><DIV CLASS=JDHint> 1398 * This parameter may not be ignored. If the numbers in the Text-File are to be interpreted as 1399 * standard {@code 'decimal'} (<I>Base 10</I>) numbers, then the user should simply pass the 1400 * constant {@code '10'} to this parameter. 1401 * </DIV> 1402 * 1403 * @return This method shall return a {@code java.util.stream.IntStream} consisting of the 1404 * integers that were found within the Text-File provided by {@code 'fileName'}. 1405 * 1406 * <BR /><BR /><DIV CLASS=JDHintAlt> 1407 * An instance of {@code IntStream} is easily converted to an {@code int[]} array using the 1408 * method {@code IntStream.toArray()}. 1409 * </DIV> 1410 * 1411 * @throws FileNotFoundException If the file named by parameter {@code 'fileName'} is not 1412 * found or not accessible in the File-System, then this exception will throw. 1413 * 1414 * @throws IOException This exception throws if there are any errors that occur while 1415 * reading this file from the File-System. 1416 * 1417 * @throws NumberFormatException If any of the numbers read from the Text-File are not 1418 * properly formatted, then this exception shall throw. 1419 * 1420 * @see StringParse#NUMBER_COMMMA_REGEX 1421 * @see StringParse#COMMA_REGEX 1422 * @see StringParse#WHITE_SPACE_REGEX 1423 */ 1424 public static IntStream readIntsFromFile 1425 (String fileName, boolean hasCommasInInts, boolean isCSV, int radix) 1426 throws FileNotFoundException, IOException 1427 { 1428 FileReader fr = new FileReader(fileName); 1429 BufferedReader br = new BufferedReader(fr); 1430 IntStream.Builder b = IntStream.builder(); 1431 String s = ""; 1432 1433 while ((s = br.readLine()) != null) 1434 { 1435 // Skip blank lines. 1436 if ((s = s.trim()).length() == 0) continue; 1437 1438 // This line simply finds String-Matches that match "Digit,Digit" and replaces 1439 // such matches with "DigitDigit". After this replacement, they are parsed with ease. 1440 // NOTE: NUMBER_COMMMA_REGEX = Pattern.compile("\\d,\\d"); 1441 1442 if (hasCommasInInts) 1443 s = StringParse.NUMBER_COMMMA_REGEX.matcher(s).replaceAll("$1$2").trim(); 1444 1445 String[] numbers = isCSV 1446 ? StringParse.COMMA_REGEX.split(s) 1447 : StringParse.WHITE_SPACE_REGEX.split(s); 1448 1449 for (String number : numbers) 1450 1451 if ((number = number.trim()).length() > 0) 1452 b.accept(Integer.parseInt(number, radix)); 1453 } 1454 1455 br.close(); 1456 fr.close(); 1457 return b.build(); 1458 } 1459 1460 /** 1461 * This may read a Text-File containing integer data. If this data is a <B>Comma Separated 1462 * Value</B> {@code 'CSV'} Text-File, please pass {@code TRUE} to the parameter 1463 * {@code 'isCSV'}. If this file contains integers that have commas between digits in groups 1464 * of three (like {@code '30,000'}) pleas pass {@code TRUE} to the parameter 1465 * {@code 'hasCommasInLongs'}. 1466 * 1467 * <EMBED CLASS='external-html' DATA-TYPE=long DATA-FILE-ID=FRW_READNUM_FFORMAT> 1468 * 1469 * <BR /><BR /><DIV CLASS=JDHint> 1470 * <B STYLE='color: red;'>Number-Format:</B> The numbers in this Text-File must be parse-able 1471 * by Java class {@code class java.lang.Long} using the method 1472 * {@code Long.parseLong(String s, int radix)} 1473 * </DIV> 1474 * 1475 * @param fileName This should contain the File-Name which itself contains a list of 1476 * {@code 'long'} integers. These {@code long} integers may be separated by either a comma 1477 * ({@code ','}) or a space ({@code ' '}). 1478 * 1479 * @param hasCommasInLongs It is allowed that the file named by {@code 'fileName'} contain 1480 * {@code long}-integers which use the commonly found notation of having a comma between groups 1481 * of three digits within a {@code long} integer. For instance the number {@code '98765'}, to 1482 * a reader, is often represented as {@code '98,765'}. When this parameter is set to 1483 * {@code TRUE}, this method shall simply remove any comma that is found juxtaposed between 1484 * two digits before processing any text found in the file. 1485 * 1486 * @param isCSV If the text file named by {@code 'fileName'} is a <B>Comma Separated Value</B> 1487 * file, then please pass {@code TRUE} to this parameter. If {@code FALSE} is passed here, 1488 * then it is mandatory that the individual numbers inside the Text-File are separated by at 1489 * least one white-space character. 1490 * 1491 * <BR /><BR /><DIV CLASS=JDHintAlt> 1492 * <B STYLE='color: red'>Important:</B> If it is decided to set both of the boolean parameters 1493 * to {@code TRUE} - <I>where the {@code long} integers have commas, 1494 * <B STYLE='color: red'>and</B> the {@code long} integers are separated by commas</I>, it is 1495 * up to the programmer to ensure that the individual numbers, themselves, are <I>not only</I> 1496 * separated by a comma, <I>but also</I> separated by a space as well. 1497 * </DIV> 1498 * 1499 * @param radix This is the {@code 'radix'}, which is also usually called the number's 1500 * {@code 'base'} that is to be used when parsing the numbers. Since Java's {@code class 1501 * Long} is used to perform the parse, <I>both</I> the {@code 'radix'}, <I>and</I> the data 1502 * found in the Text-File must conform to the Java method 1503 * {@code Long.parseLong(String s, int radix)}. 1504 * 1505 * <BR /><BR /><DIV CLASS=JDHint> 1506 * This parameter may not be ignored. If the numbers in the Text-File are to be interpreted as 1507 * standard {@code 'decimal'} (<I>Base 10</I>) numbers, then the user should simply pass the 1508 * constant {@code '10'} to this parameter. 1509 * </DIV> 1510 * 1511 * @return This method shall return a {@code java.util.stream.LongStream} consisting of the 1512 * {@code long}-integers that were found within the Text-File provided by {@code 'fileName'}. 1513 * 1514 * <BR /><BR /><DIV CLASS=JDHintAlt> 1515 * An instance of {@code LongStream} is easily converted to a {@code long[]} array using the 1516 * method {@code LongStream.toArray()}. 1517 * </DIV> 1518 * 1519 * @throws FileNotFoundException If the file named by parameter {@code 'fileName'} is not 1520 * found or not accessible in the File-System, then this exception will throw. 1521 * 1522 * @throws IOException This exception throws if there are any errors that occur while 1523 * reading this file from the File-System. 1524 * 1525 * @throws NumberFormatException If any of the numbers read from the Text-File are not 1526 * properly formatted, then this exception shall throw. 1527 * 1528 * @see StringParse#NUMBER_COMMMA_REGEX 1529 * @see StringParse#COMMA_REGEX 1530 * @see StringParse#WHITE_SPACE_REGEX 1531 */ 1532 public static LongStream readLongsFromFile 1533 (String fileName, boolean hasCommasInLongs, boolean isCSV, int radix) 1534 throws FileNotFoundException, IOException 1535 { 1536 FileReader fr = new FileReader(fileName); 1537 BufferedReader br = new BufferedReader(fr); 1538 LongStream.Builder b = LongStream.builder(); 1539 String s = ""; 1540 1541 while ((s = br.readLine()) != null) 1542 { 1543 // Skip blank lines. 1544 if ((s = s.trim()).length() == 0) continue; 1545 1546 // This line simply finds String-Matches that match "Digit,Digit" and replaces 1547 // such matches with "DigitDigit". After this replacement, they are parsed with ease. 1548 // NOTE: NUMBER_COMMMA_REGEX = Pattern.compile("\\d,\\d"); 1549 1550 if (hasCommasInLongs) 1551 s = StringParse.NUMBER_COMMMA_REGEX.matcher(s).replaceAll("$1$2").trim(); 1552 1553 String[] numbers = isCSV 1554 ? StringParse.COMMA_REGEX.split(s) 1555 : StringParse.WHITE_SPACE_REGEX.split(s); 1556 1557 for (String number : numbers) 1558 if ((number = number.trim()).length() > 0) 1559 b.accept(Long.parseLong(number, radix)); 1560 } 1561 1562 br.close(); 1563 fr.close(); 1564 return b.build(); 1565 } 1566 1567 /** 1568 * This may read a Text-File containing floating-point data. If this data is a <B>Comma 1569 * Separated Value</B> {@code 'CSV'} Text-File, please pass {@code TRUE} to the parameter 1570 * {@code 'isCSV'}. If this file contains {@code double's} that have commas between digits 1571 * in groups of three (like {@code '30,000,000,00'}) pleas pass {@code TRUE} to the parameter 1572 * {@code 'hasCommasInDoubles'}. 1573 * 1574 * <EMBED CLASS='external-html' DATA-TYPE=long DATA-FILE-ID=FRW_READNUM_FFORMAT> 1575 * 1576 * <BR /><BR /><DIV CLASS=JDHint> 1577 * <B STYLE='color: red;'>Number-Format:</B> The numbers in this Text-File must be parse-able 1578 * by Java class {@code class java.lang.Double} using the method 1579 * {@code Double.parseDouble(String s)} 1580 * </DIV> 1581 * 1582 * @param fileName This should contain the File-Name which itself contains a list of 1583 * {@code 'double'} values. These {@code double's} may be separated by either a comma 1584 * ({@code ','}) or a space ({@code ' '}). 1585 * 1586 * @param hasCommasInDoubles It is allowed that the file named by {@code 'fileName'} contain 1587 * {@code double}-values which use the commonly found notation of having a comma between groups 1588 * of three digits within a {@code double} value. For instance the number {@code '98765.01'}, 1589 * to a reader, can be represented as {@code '98,765.01'}. When this parameter is set to 1590 * {@code TRUE}, this method shall simply remove any comma that is found juxtaposed between 1591 * two digits before processing any text found in the file. 1592 * 1593 * @param isCSV If the text file named by {@code 'fileName'} is a <B>Comma Separated Value</B> 1594 * file, then please pass {@code TRUE} to this parameter. If {@code FALSE} is passed here, 1595 * then it is mandatory that the individual numbers inside the Text-File are separated by at 1596 * least one white-space character. 1597 * 1598 * <BR /><BR /><DIV CLASS=JDHintAlt> 1599 * <B STYLE="color: red">Important:</B> If it is decided to set both of the boolean parameters 1600 * to {@code TRUE} - <I>where the {@code double} values have commas, 1601 * <B STYLE="color: red">and</B> the {@code double} values are separated by commas</I>, it is 1602 * up to the programmer to ensure that the individual numbers, themselves, are <I>not only</I> 1603 * separated by a comma, <I>but also</I> separated by a space as well. 1604 * </DIV> 1605 * 1606 * @return This method shall return a {@code java.util.stream.DoubleStream} consisting of the 1607 * {@code double}-values that were found within the Text-File provided by {@code 'fileName'}. 1608 * 1609 * <BR /><BR /><DIV CLASS=JDHint> 1610 * An instance of {@code DoubleStream} is easily converted to a {@code double[]} array using 1611 * the method {@code DoubleStream.toArray()}. 1612 * </DIV> 1613 * 1614 * @throws FileNotFoundException If the file named by parameter {@code 'fileName'} is not 1615 * found or not accessible in the File-System, then this exception will throw. 1616 * 1617 * @throws IOException This exception throws if there are any errors that occur while 1618 * reading this file from the File-System. 1619 * 1620 * @throws NumberFormatException If any of the numbers read from the Text-File are not 1621 * properly formatted, then this exception shall throw. 1622 * 1623 * @see StringParse#NUMBER_COMMMA_REGEX 1624 * @see StringParse#COMMA_REGEX 1625 * @see StringParse#WHITE_SPACE_REGEX 1626 */ 1627 public static DoubleStream readDoublesFromFile 1628 (String fileName, boolean hasCommasInDoubles, boolean isCSV) 1629 throws FileNotFoundException, IOException 1630 { 1631 FileReader fr = new FileReader(fileName); 1632 BufferedReader br = new BufferedReader(fr); 1633 DoubleStream.Builder b = DoubleStream.builder(); 1634 String s = ""; 1635 1636 while ((s = br.readLine()) != null) 1637 { 1638 // Skip blank lines. 1639 if ((s = s.trim()).length() == 0) continue; 1640 1641 // This line simply finds String-Matches that match "Digit,Digit" and replaces 1642 // such matches with "DigitDigit". After this replacement, they are parsed with ease. 1643 // NOTE: NUMBER_COMMMA_REGEX = Pattern.compile("\\d,\\d"); 1644 1645 if (hasCommasInDoubles) 1646 s = StringParse.NUMBER_COMMMA_REGEX.matcher(s).replaceAll("$1$2").trim(); 1647 1648 String[] numbers = isCSV 1649 ? StringParse.COMMA_REGEX.split(s) 1650 : StringParse.WHITE_SPACE_REGEX.split(s); 1651 1652 for (String number : numbers) 1653 1654 if ((number = number.trim()).length() > 0) 1655 b.accept(Double.parseDouble(number)); 1656 } 1657 1658 br.close(); 1659 fr.close(); 1660 return b.build(); 1661 } 1662 1663 /** 1664 * Convenience Method. 1665 * <BR />Invokes: {@code java.io.FileOutputStream.write(byte[])}. 1666 * <BR /><B>NOTE:</B> This may throw {@code IOException, FileNotFoundException}, etc... 1667 */ 1668 public static void writeBinary(byte[] bArr, String fileName) throws IOException 1669 { 1670 // The input-stream is 'java.lang.AutoCloseable'. 1671 // 1672 // NOTE: The IOException will still be thrown out of this method if it occurs. It is not 1673 // caught! This also (potentially) throws SecurityException & FileNotFoundException 1674 1675 try 1676 (FileOutputStream fos = new FileOutputStream(fileName)) 1677 { fos.write(bArr); } 1678 } 1679 1680 1681 /** 1682 * Convenience Method. 1683 * <BR />Invokes: {@code java.io.FileOutputStream.write(byte[], int, int)}. 1684 * <BR /><B>NOTE:</B> This may throw {@code IOException, IndexOutOfBoundsException}, etc... 1685 */ 1686 public static void writeBinary(byte[] bArr, int offset, int len, String fileName) 1687 throws IOException 1688 { 1689 // The input-stream is 'java.lang.AutoCloseable'. 1690 // 1691 // NOTE: The IOException will still be thrown out of this method if it occurs. It is not 1692 // caught! This also (potentially) throws SecurityException, FileNotFoundException, 1693 // IndexOutOfBoundsExcepton (if 'offset' or 'len' do not adhere to 'bArr' definition) 1694 1695 try 1696 (FileOutputStream fos = new FileOutputStream(fileName)) 1697 { fos.write(bArr, offset, len); } 1698 } 1699 1700 /** 1701 * Convenience Method. 1702 * <BR />Invokes: {@link #readBinary(String, int, int)}. 1703 */ 1704 public static byte[] readBinary(String fileName) 1705 throws FileNotFoundException, IOException 1706 { return readBinary(fileName, 0, -1); } 1707 1708 /** 1709 * Reads data from a binary file into a Java {@code byte[]} array. 1710 * 1711 * <BR /><BR />Unlike Java's {@code FileOutputStream.write(...)} method, the {@code read(...)} 1712 * Java provides in that same exact class is more difficult to use. This method is much longer 1713 * than its corresponding {@link #writeBinary(byte[], String)} method. 1714 * 1715 * @param fileName The name of the file (on the File-System) to read. 1716 * 1717 * @param offset The number of {@code byte's} to skip before appending a {@code byte} into the 1718 * output {@code byte[]} array. If the value provided to parameer {@code 'offset'} is longer 1719 * than the size of the file itself, then a <B>zero-length {@code byte[]} array</B> will be 1720 * returned. 1721 * 1722 * <BR /><BR /><DIV CLASS=JDHint> 1723 * The meaning of the value in parameter {@code 'offset'} is very different from the meaning of 1724 * a parameter by that exact same name, except in method {@code read(...)} of class 1725 * {@code FileInputStream}. <B>HERE</B> the offset is the number of <I>bytes to skip inside of 1726 * file {@code 'fileName'}, before saving the values that are read froom disk.</I> In the 1727 * {@code FileInputStream}, offset is used as an array pointer. 1728 * </DIV> 1729 * 1730 * @param len Once the internal-loop has begun copying bytes from the Data-File into the 1731 * returned {@code byte[]} array, {@code byte's} will continue to be copied into this array 1732 * until precisely {@code 'len'} bytes have been copied. 1733 * 1734 * <BR /><BR /><DIV CLASS=JDHintAlt> 1735 * The user may provide any negative number to this parameter, and the read process will simply 1736 * begin at position {@code 'offset'}, and continue reading until the End of the File has been 1737 * reached. 1738 * </DIV> 1739 * 1740 * @return Returns a {@code byte[]}-array, with a length of (parameter) {@code 'len'} bytes. 1741 * 1742 * @throws FileNotFoundException Class {@code java.io.FileInputStream} will throw a 1743 * {@code FileNotFoundException} if that class is passed a {@code 'fileName'} that does not 1744 * exist, or is a File-Name that represents a directory not a file. 1745 * 1746 * @throws SecurityException If a security manager exists and its {@code checkRead} method 1747 * denies read access to the file. 1748 * 1749 * @throws IOException The {@code FileInputStream} instance, and the {@code java.io.File} 1750 * instance are both capable of throwing {@code IOException}. 1751 * 1752 * @throws IllegalArgumentException 1753 * 1754 * <BR /><BR /><UL CLASS=JDUL> 1755 * <LI> The value provided to {@code 'offset'} is negative</LI> 1756 * <LI> The value provided to parameter {@code 'len'} is zero.</LI> 1757 * </UL> 1758 * 1759 * @throws FileSizeException This exception throws if any of the following are detected: 1760 * 1761 * <BR /><BR /><UL CLASS=JDUL> 1762 * 1763 * <LI> The returned {@code byte[]} array cannot have a size larger than 1764 * {@code Integer.MAX_VALUE}. If the returned array would have a larger size - <I>based 1765 * on any combination of provided input values</I>, then this exception will throw. 1766 * <BR /><BR /> 1767 * </LI> 1768 * 1769 * <LI> The value provided to {@code 'offset'} is larger than the size of the underlying 1770 * file that is specified by parameter {@code 'fileName'} 1771 * <BR /><BR /> 1772 * </LI> 1773 * 1774 * <LI> The total of {@code offset + len} is larger than the size of the underlying file. 1775 * <BR /><BR /> 1776 * </LI> 1777 * 1778 * <LI> An invocation of the method {@code File.length()} returns zero, indicating that the 1779 * file is either unreadable, non-existant, or possibly empty. 1780 * </LI> 1781 * 1782 * </UL> 1783 * 1784 * @throws EOFException This particular exception should not be expected. Before any reads are 1785 * done, the size of the Data-File is first checked to see if it is big enough to have the 1786 * amount of data that is requested by input paramters {@code 'offset'} and {@code 'len'}. If 1787 * however, an error occurrs, and the Operating System returns an {@code EOF} earlier than 1788 * expected (for unforseen reasons), then {@code EOFException} would throw. 1789 */ 1790 public static byte[] readBinary(String fileName, int offset, int len) 1791 throws FileNotFoundException, IOException 1792 { 1793 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1794 // EXCEPTION CHECKS 1795 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1796 1797 if (offset < 0) throw new IllegalArgumentException 1798 ("The value of offset (" + offset + ") is negative."); 1799 1800 if (len == 0) throw new IllegalArgumentException 1801 ("A value of zero was provided to parameter 'len.' This is not allowed"); 1802 1803 File f = new File(fileName); 1804 1805 try 1806 (FileInputStream fis = new FileInputStream(f)) 1807 { 1808 long fileLen = f.length(); 1809 1810 // A file-name that points to a completely empty file was passed to parameter 'fileName' 1811 // **OR** the operating system returned 0 for some other error-related reason. 1812 // According to the Java-Doc explanation, a '0' returned value might mean there were 1813 // errors when trying to access the file. 1814 1815 if (fileLen == 0) throw new FileSizeException( 1816 "Calling java.io.File.length() returned 0 for the file-name that was " + 1817 "provided to this method:\n[" + fileName + "]. This might mean that the file " + 1818 "does not exist, or has other issues.", 1819 0 1820 ); 1821 1822 // The file would end before we have even started reading bytes - after having skipped 1823 // 'offset' nmber of initial bytes. 1824 1825 if (offset > fileLen) throw new FileSizeException( 1826 "The value of offset (" + offset + ") is larger than the size of the binary " + 1827 "file, which only had size (" + fileLen + ").", 1828 fileLen 1829 ); 1830 1831 // If 'len' was passed a negative value, that value is actually meaningless - and was 1832 // just used to indicate that reading the entire file starting at byte # 'offset' is 1833 // what the user is requesting. 1834 1835 if (len > 0) 1836 1837 // This simply checks how many bytes the file would need to have to provide for 1838 // reading from 'offset' up until 'offset + len' 1839 1840 if ((offset + len) > fileLen) throw new FileSizeException( 1841 "The file [" + fileName + "] apparently has a size of [" + fileLen + "], " + 1842 "but the offset (" + offset + ") and the length (" + len + ") that was " + 1843 "requested sum to (" + (offset+len) + "), which is greater than that size.", 1844 fileLen 1845 ); 1846 1847 1848 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1849 // More Exception Checks: Check what the size of the returned byte[] array will be 1850 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1851 1852 // Negative-Length means that bytes are read until an EOF occurrs 1853 if (len < 0) 1854 { 1855 long bytesToRead = fileLen - offset; 1856 1857 if (bytesToRead > Integer.MAX_VALUE) throw new FileSizeException( 1858 "This file has [" + fileLen + "] bytes of data, and even with an offset of " + 1859 '[' + offset + "], there are still " + bytesToRead + " bytes of data to " + 1860 "place into the array. This value is larger than Integer.MAX_VALUE " + 1861 "(" + Integer.MAX_VALUE + ").", 1862 fileLen 1863 ); 1864 1865 else len = (int) bytesToRead; 1866 } 1867 1868 // A Positive length means that exactly the value inside input-parameter 'len' need to 1869 // be placed into the returned byte[] array. 1870 1871 else if (len > Integer.MAX_VALUE) throw new FileSizeException( 1872 "This file has [" + fileLen + "] bytes of data, which is larger than " + 1873 "Integer.MAX_VALUE (" + Integer.MAX_VALUE + ").", 1874 fileLen 1875 ); 1876 1877 1878 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1879 // FIRST, if there is a NON-ZERO OFFSET, then exactly OFFSET bytes must be skipped 1880 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1881 1882 int failedReads = 0; 1883 long bytesSkipped = 0; 1884 int totalBytesSkipped = 0; 1885 int bytesRemaining = offset; 1886 1887 // NOTE: This loop is skipped, immediately / automatically, if indeed 'offset' is zero 1888 while (totalBytesSkipped < offset) 1889 1890 if ((bytesSkipped = fis.skip(bytesRemaining)) == 0) 1891 { 1892 if (failedReads++ == 10) throw new IOException( 1893 "There have 10 repeated attempts to read the file's data, but all " + 1894 "attempts have resulted in empty-reads with zero bytes being retrieved " + 1895 "from disk." 1896 ); 1897 } 1898 else 1899 { 1900 // I don't see why this should happen, but it will be left here, just in 1901 // case Java screws up. 1902 1903 if (bytesSkipped > bytesRemaining) throw new InternalError( 1904 "This error is being thrown because the Java Virtual Machine has " + 1905 "skipped past the end of the requested offset. This has occured while " + 1906 "calling FileInputStream.skip(offset)." 1907 ); 1908 1909 // NOTE: I am *FULLY AWARE* this is redundant, but the variable names are 1910 // the only thing that is very readable about this method. 1911 1912 totalBytesSkipped += bytesSkipped; 1913 bytesRemaining -= bytesSkipped; 1914 } 1915 1916 1917 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1918 // THEN, Read the bytes from the file 1919 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1920 1921 byte[] retArr = new byte[len]; 1922 int arrPos = 0; 1923 1924 // This one was already defined / declared in the previous part. Initialize it, but 1925 // don't re-declare it. 1926 bytesRemaining = len; 1927 1928 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1929 // This loop exits when all (requested) bytes have been read, or EOFException! 1930 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1931 1932 while (bytesRemaining > 0) 1933 { 1934 int bytesRead = fis.read(retArr, arrPos, bytesRemaining); 1935 1936 1937 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1938 // Case 1: The EOF Marker was reached, before filling up the response-array 1939 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1940 1941 if (bytesRead == -1) if (bytesRemaining > 0) throw new EOFException( 1942 "The end of file '" + fileName + "' was reached before " + len + 1943 " bytes were read. Only " + arrPos + " bytes were read." 1944 ); 1945 1946 1947 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1948 // Yes, this seems redundant, but it's just the way it is 1949 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1950 // 1951 // arrPos ==> 0 ... retArr.length (retArr.length => input-param 'len') 1952 // bytesRemaining ==> 'len' ... 0 1953 1954 arrPos += bytesRead; 1955 bytesRemaining -= bytesRead; 1956 1957 1958 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1959 // Case 2: Exactly the appropriate number of bytes were read. 1960 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1961 1962 if (arrPos == retArr.length) return retArr; 1963 1964 1965 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1966 // Case 3: This should not be reachable! 1967 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 1968 // 1969 // Java's FileInputStream.read method is supposed to stop when it has read 1970 // 'bytesRemaining' number of bytes! 1971 1972 if (arrPos > retArr.length) throw new UnreachableError(); 1973 } 1974 1975 // One of the cases inside the loop body must fire before the loop ever actually exits, 1976 // unless I'm missing something! 1977 throw new UnreachableError(); 1978 } 1979 } 1980 1981 /** 1982 * Attempts to read a {@code java.lang.Class} from a class-file. 1983 * 1984 * <BR /><BR />It is not neccessarily always knownst to the programmer what the 1985 * <I>Full-Package Class-Name</I> of the {@code class} that's inside the class-file actually 1986 * is. 1987 * 1988 * @param classFileName The name of any class-file on the File-System. 1989 * 1990 * @param possibleClassNames If the exact <B STYLE='color: red;'>Full-Package Name</B> of the 1991 * class being read is known, then that ought to be the only {@code String} passed to this 1992 * Var-Args {@code String}-Parameter. If there are multiple possibilities, pass all of them, 1993 * and all we be used in an attempt to parse & load this class. 1994 * 1995 * <BR /><BR /><B>DEVELOPER NOTE:</B> Perhaps I am stupid, but I cannot find any way (using the 1996 * standard JDK) to read a class file, and then ask that class-file either: 1997 * 1998 * <BR /><BR /><UL CLASS=JDUL> 1999 * <LI>What the name of the {@code Class} in the Class-File is?</LI> 2000 * <LI>What the name of the {@code Package} being used in that Class-File is?</LI> 2001 * </UL> 2002 * 2003 * <BR />So, for now, a Var-Args {@code String}-Array is required. 2004 * 2005 * <BR /><BR />To add insult to injury, the standard Java Exception class 2006 * {@code 'TypeNotPresentException'} doesn't have a constructor that accepts a 2007 * {@code 'message'} parameter (it accepts a {@code 'typeName'}) parameter instead. As a 2008 * result, if this method throws that exception, the error-message printed has a few 2009 * 'extranneous characters' <B>BOTH</B> before the actual message, <B>AND</B> after it. It is 2010 * staying this way because Java's Description of that exception matches precisely with its 2011 * use here. 2012 * 2013 * @return An instance of {@code java.lang.Class} that was contained by the Class-File. 2014 * 2015 * @throws IOException Java may throw several exceptions while attempting to load 2016 * the {@code '.class'} file into a {@code byte[]} array. Such exceptions may include 2017 * {@code IOException}, {@code SecurityException}, {@code FileNotFoundException} etc... 2018 * 2019 * @throws TypeNotPresentException If none of the names listed in Var-Args {@code String[]} 2020 * Array parameter {@code 'possibleClassNames'} are consistent with 2021 * <B STYLE='color:red'><I>BOTH</I></B> the package-name <B STYLE='color:red;'>AND</B> the 2022 * class-name of the actual class that resides inside {@code 'classFileName'}, then this 2023 * exception will throw. 2024 * 2025 * <BR /><BR /><DIV CLASS=JDHint> 2026 * <B STYLE='color:red'>Note:</B> Rather than simply returning 'null', this 2027 * {@code RuntimeException} is thrown as a nicely worded error message is provided. 2028 * </DIV> 2029 */ 2030 public static Class<?> readClass(String classFileName, String... possibleClassNames) 2031 throws IOException 2032 { 2033 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 2034 // Load the entire '.class' file into a byte[] array. 2035 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 2036 2037 // Now read the bytes directly into a byte[] array, courtesy of Torello.Java.FileRW 2038 byte[] byteArray = readBinary(classFileName); 2039 2040 2041 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 2042 // Create a ClassLoader - and convert "byte[]" into "Class<?> summarySorterClass" 2043 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 2044 2045 // NOTE: The commented lines below will not work if there is a "Package Name" given to the 2046 // class (in the '.java' file). It is better to just read the raw class-file bytes 2047 // into a Java byte[] array. SPECIFICALLY, if class is not inside of a directory 2048 // that is PRECISELY-CONSISTENT with the full-path package+class name, then the class 2049 // loader listed below will FAIL. (NOTE: If the package+class name is consisten, 2050 // then a programmer wouldn't need ANY OF THIS, and could simply call the normal 2051 // "Class.forName(fullPackageClassName)" to get the class!) 2052 // 2053 // ClassLoader cl = new URLClassLoader(new URL[] { new URL("file://" + path) }); 2054 // Class<?> ret = cl.loadClass(className); 2055 // 2056 // HOWEVER: Creating a new ClassLoader instance that accepts the byte-array (thereby 2057 // exporting the 'protected' method defineClass) *DOES* work. 2058 2059 class ByteClassLoader extends ClassLoader 2060 { 2061 public Class<?> defineClass(String name, byte[] classBytes) 2062 { return defineClass(name, classBytes, 0, classBytes.length); } 2063 } 2064 2065 ByteClassLoader cl = new ByteClassLoader(); 2066 Class<?> ret = null; 2067 StringBuilder sb = new StringBuilder(); 2068 2069 2070 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 2071 // Make attempts to convert the byte[] array into a java.lang.Class 2072 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 2073 2074 for (String fullClassName : possibleClassNames) 2075 2076 try 2077 { return cl.defineClass(fullClassName, byteArray); } 2078 2079 catch (NoClassDefFoundError e) 2080 { 2081 String errorMessage = e.getMessage(); 2082 2083 if (errorMessage != null) sb.append(errorMessage + '\n'); 2084 } 2085 2086 throw new TypeNotPresentException( 2087 "The Class-File: [" + classFileName + "] seems to have been successfully read, but " + 2088 "none of the user provided Canonical-Class Names (including a package) were " + 2089 "consistent with the actual name of the Class/Type inside that class file. Below " + 2090 "are listed the exception-messages received, which include both the actual/complete " + 2091 "canonical class-name, along with your user-provided guesses. Errors Saved:\n" + 2092 StrIndent.indent(sb.toString(), 4), 2093 null 2094 ); 2095 } 2096}