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