001package Torello.Java;
002
003import Torello.Java.StrIndent;
004
005import static Torello.Java.C.*;
006
007import java.util.*;
008import java.util.stream.Stream;
009import java.io.*;
010import java.util.zip.*;
011
012import com.google.cloud.storage.*;
013import com.google.auth.oauth2.*;
014
015/**
016 * <B><CODE>Load File Exception Catch</CODE></B> provides an eloquent way for printing standardized
017 * <I>(consistent-looking)</I> error messages to terminal if a data-file fails to load from the
018 * file-system, and subsequently halting the JVM - immediately.
019 * 
020 * <EMBED CLASS='external-html' DATA-FILE-ID=LFEC>
021 */
022@Torello.JavaDoc.StaticFunctional
023public class LFEC
024{
025    private LFEC() { }
026
027
028    // ********************************************************************************************
029    // ********************************************************************************************
030    // These are error-printing methods.
031    // ********************************************************************************************
032    // ********************************************************************************************
033
034
035    /**
036     * This prints a message and a location and halts the current thread immediately.  (Halts the
037     * program)  The entire rational behind the {@code class LFEC} is to facilitate loading
038     * data-files, and forcing an immediate program halt if this operation fails.  Generally,
039     * during development and debugging, failing to read from a data-file is serious enough to
040     * warrant stopping the software until the issue is fixed.  "Hobbling along without reading
041     * the data-files" makes little sense.
042     *
043     * <BR /><BR />When the software is being released or distributed, the philosophy turns to
044     * loading mission critical data out of data-files in a java jar-file.  If the jar-file is
045     * corrupted and those files are not present, generally <I>this situation would <B>also
046     * warrant</B> halting the program execution immediately until the jar file is fixed.</I>
047     *
048     * @param t An exception, error or other {@code Throwable} that generated a problem when
049     * attempting to read a data-file.
050     * 
051     * @param message This is an internally generated message explaining what has occurred.
052     */
053    protected static void ERROR_EXIT(Throwable t, String message)
054    {
055        System.out.println(
056            '\n' +
057            "There was an error loading a data-file, and program-execution is being halted " +
058            "immediately.\n" +
059            "Problem Encountered With:\n" + StrIndent.indent(message, 4) + "\n" +
060            "Exception or Error Message:\n" +
061            EXCC.toString(t) + "\n\n" +
062            "Exiting Program, Fatal Error Loading Critical Data File."
063        );
064
065        System.exit(1);
066    }
067
068    /**
069     * This prints a message and a location and halts the current thread immediately.  (Halts the
070     * program)  The entire rational behind the {@code class LFEC} is to facilitate loading
071     * data-files, and forcing an immediate program halt if this operation fails.  Generally,
072     * during development and debugging, failing to read from a data-file is serious enough to
073     * warrant stopping the software until the issue is fixed.  "Hobbling along without reading
074     * the data-files" makes little sense.
075     *
076     * <BR /><BR />When the software is being released or distributed, the philosophy turns to
077     * loading mission critical data out of data-files in a java jar-file.  If the jar-file is
078     * corrupted and those files are not present, generally <I>this situation would <B>also
079     * warrant</B> halting the program execution immediately until the jar file is fixed.</I>
080     *
081     * @param message This is an internally generated message explaining what has occurred.
082     */
083    protected static void ERROR_EXIT(String message)
084    {
085        System.out.println(
086            '\n' +
087            "There was an error loading a data-file, and program-execution is being halted " +
088            "immediately.\n" +
089            "Problem Encountered With:\n" + message + "\n\n" +
090            "Exiting Program, Fatal Error Loading Critical Data File."
091        );
092
093        System.exit(1);
094    }
095
096
097    // ********************************************************************************************
098    // ********************************************************************************************
099    // These are the data-loading methods.
100    // ********************************************************************************************
101    // ********************************************************************************************
102
103
104    /**
105     * Loads a file directly into a java-{@code String.}  If this file fails to load, it halts the
106     * run-time environment.
107     * 
108     * @param f This is a {@code java.lang.String} that contains the filename.
109     * 
110     * @return The returned {@code String} contains the entire contents of the file.
111     * 
112     * @see FileRW#loadFileToString(String)
113     * @see #ERROR_EXIT(Throwable, String)
114     */
115    public static String loadFile(String f)
116    {
117        try
118            { return FileRW.loadFileToString(f); }
119
120        catch (Throwable t)
121            { ERROR_EXIT(t, "FileRW.loadFileToString(\"" + f + "\")");  }
122
123        throw new UnreachableError(); // Should not be possible to reach this statement
124    }
125
126    /**
127     * Loads a file directory to a string <I>from a java jar-file</I>.  It halts the program, and
128     * prints a detailed message if any {@code Error's} or {@code Exception's} were thrown.  The
129     * directory inside the jar-file where this file may be located identified by parameter
130     * {@code class 'classLoaderClass'}.
131     *
132     * @param classLoaderClass
133     * <EMBED CLASS='external-html' DATA-FILE-ID=LFEC_CLASS_LOAD_C>
134     * <EMBED CLASS='external-html' DATA-FILE-ID=RAWTYPES>
135     *
136     * @param f This is a {@code String} that contains the filename of the data-file that needs
137     * to be loaded.  It is a 'relative file-name' that is relative to the jar-file / directory pair
138     * location that the class loader identifies using the {@code Class} from parameter
139     * {@code 'classLoaderClass'}
140     *
141     * @return The returned string contains the entire contents of the file.
142     * 
143     * @see #ERROR_EXIT(Throwable, String)
144     */
145    public static String loadFile_JAR(Class<?> classLoaderClass, String f)
146    {
147        // These are 'java.lang.AutoCloseable', and java handles them automatically
148        // if there is an exception
149
150        try (
151            InputStream     is  = classLoaderClass.getResourceAsStream(f);
152            BufferedReader  br  = new BufferedReader(new InputStreamReader(is));
153        )
154        {
155            String          s   = "";
156            StringBuffer    sb  = new StringBuffer();
157
158            while ((s = br.readLine()) != null) sb.append(s + "\n");
159
160            return sb.toString();
161        }
162
163        catch (Throwable t)
164        {
165            ERROR_EXIT(
166                t,
167                "Error loading text-file [" + f + "] from jar-file.\n" +
168                "Class loader attempted to use information in class " +
169                "[" + classLoaderClass.getCanonicalName() + "], but failed."
170            );
171        }
172
173        throw new UnreachableError(); // Should NOT be possible to reach this statement...
174    }
175
176    /**
177     * Loads a file <I>from a java jar-file</I> using an {@code InputStream} and copies that file,
178     * byte-for-byte, to {@code 'targetFileName'}.  A detailed message is printed if any
179     * {@code Error's} or {@code Exception's} were thrown.  The directory inside the jar-file
180     * where this file may be located identified by parameter {@code class 'classLoaderClass'}.
181     *
182     * @param classLoaderClass <EMBED CLASS='external-html' DATA-FILE-ID=LFEC_CLASS_LOAD_C>
183     * <EMBED CLASS='external-html' DATA-FILE-ID=RAWTYPES>
184     *
185     * @param fileName This is a {@code String} that contains the filename of the data-file that
186     * needs to be copied.  It is a 'relative file-name' that is relative to the jar-file /
187     * directory pair location that the class loader identifies using the {@code Class} from
188     * parameter {@code 'classLoaderClass'}.
189     * 
190     * @param targetFileName This is the file and directory that contains the target location
191     * (as a {@code String}) to where the file should be copied.
192     * 
193     * @see #ERROR_EXIT(Throwable, String)
194     */
195    public static void copyFile_JAR
196        (Class<?> classLoaderClass, String fileName, String targetFileName)
197    {
198        // These are 'java.lang.AutoCloseable', and java handles them automatically
199        // if there is an exception
200
201        try (
202            InputStream         is  = classLoaderClass.getResourceAsStream(fileName);
203            FileOutputStream    fos = new FileOutputStream(targetFileName);
204        )
205        {
206            byte[]  b       = new byte[5000];
207            int     result  = 0;
208    
209            while ((result = is.read(b)) != -1) fos.write(b, 0, result);
210        }
211
212        catch (Throwable t)
213        {
214            ERROR_EXIT(
215                t,
216                "Error copying file [" + fileName + "] from jar-file to " +
217                "[" + targetFileName + "].\n" +
218                "Class loader attempted to use information in class " +
219                "[" + classLoaderClass.getCanonicalName() + "]."
220            );
221        }
222    }
223
224    /**
225     * This loads a file to a {@code Vector} of {@code String's}.  It halts the program, and prints
226     * a detailed message if any errors or exceptions occur.
227     *
228     * @param f The name of the file to load
229     *
230     * @param includeNewLine States whether the {@code '\n'} (new-line) character should be appended
231     * to each element of the returned {@code Vector<String>}.
232     *
233     * @return a {@code Vector} of {@code String's}, where each element in the {@code Vector} is a
234     * line retrieved from the text-file.
235     *
236     * @see #ERROR_EXIT(Throwable, String)
237     */
238    public static Vector<String> loadFileToVector(String f, boolean includeNewLine)
239    {
240        try
241            { return FileRW.loadFileToVector(f, includeNewLine); }
242
243        catch (Throwable t)
244        {
245            ERROR_EXIT(
246                t, 
247                "Attempting to Invoke this Load-Method with these Arguments:\n" +
248                "FileRW.loadFileToVector(\"" + f + "\", " + includeNewLine + ");"
249            );
250        }
251
252        throw new UnreachableError(); // Should NOT be possible to reach this statement...
253    }
254
255
256    /**
257     * This loads a file to a {@code Stream} of {@code String's}.  It halts the program, and prints
258     * a detailed message if any errors or exceptions occur.
259     *
260     * @param f The name of the file to load
261     *
262     * @param includeNewLine States whether the {@code '\n'} (new-line) character should be appended
263     * to each element of the returned {@code Vector<String>}.
264     *
265     * @return a {@code Stream} of {@code String's}, where each element in the {@code Stream} is a
266     * line retrieved from the text-file.
267     *
268     * @see #ERROR_EXIT(Throwable, String)
269     */
270    public static Stream<String> loadFileToStream(String f, boolean includeNewLine)
271    {
272        try
273            { return FileRW.loadFileToStream(f, includeNewLine); }
274
275        catch (Throwable t)
276        {
277            ERROR_EXIT(
278                t, 
279                "Attempting to Invoke this Load-Method with these Arguments:\n" +
280                "FileRW.loadFileToStream(\"" + f + "\", " + includeNewLine + ");"
281            );
282        }
283
284        throw new UnreachableError(); // Should NOT be possible to reach this statement...
285    }
286
287    /**
288     * This loads a file to a {@code Vector} of {@code String's}.  It halts the program, and prints
289     * a detailed message if any {@code Error's} or {@code Exception's} occur.  The directory inside
290     * the jar-file where this file may be located identified by parameter
291     * {@code 'classLoaderClass'}.
292     *
293     * @param classLoaderClass <EMBED CLASS='external-html' DATA-FILE-ID=LFEC_CLASS_LOAD_C>
294     * <EMBED CLASS='external-html' DATA-FILE-ID=RAWTYPES>
295     *
296     * @param f This is a {@code String} that contains the filename of the data-file that needs to
297     * be loaded.  It is a 'relative file-name' that is relative to the jar-file / directory pair
298     * location that the class loader identifies using the {@code Class} from parameter
299     * {@code 'classLoaderClass'}.
300     *
301     * @param includeNewLine States whether the {@code '\n'} (newline) character should be appended
302     * to each element of the {@code Vector}.
303     *
304     * @return a {@code Vector} of {@code String's}, where each element in the {@code Vector} is a
305     * line retrieved from the text-file.
306     *
307     * @see #ERROR_EXIT(Throwable, String)
308     */
309    public static Vector<String> loadFileToVector_JAR
310        (Class<?> classLoaderClass, String f, boolean includeNewLine)
311    {
312        // These are 'java.lang.AutoCloseable', and java handles them automatically
313        // if there is an exception
314
315        try (
316            InputStream     is  = classLoaderClass.getResourceAsStream(f);
317            BufferedReader  br  = new BufferedReader(new InputStreamReader(is));
318        )
319        {
320            String          s   = "";
321            Vector<String>  ret = new Vector<>();
322
323            if (includeNewLine)
324                while ((s = br.readLine()) != null)
325                    ret.addElement(s + '\n');
326
327            else
328                while ((s = br.readLine()) != null)
329                    ret.addElement(s);
330
331            return ret;
332        }
333
334        catch (Throwable t)
335        {
336            ERROR_EXIT(
337                t,
338                "Error loading text-file [" + f + "] from jar-file.\n" +
339                "Parameter includeNewLine was: " + includeNewLine + "\n" +
340                "Class loader attempted to use information in class " +
341                "[" + classLoaderClass.getCanonicalName() + "], but failed."
342            );
343        }
344
345        throw new UnreachableError(); // Should NOT be possible to reach this statement...
346    }
347
348    /**
349     * This loads a {@code java.lang.Object} from a data-file.
350     * <EMBED CLASS='external-html' DATA-FILE-ID=LFEC_CONTAINER>
351     *
352     * @param f The name of the file containing a serialized {@code java.lang.Object} to load
353     *
354     * @param zip This should be <I>TRUE</I> if, when serialized, the {@code Object} was compressed
355     * too, and <I>FALSE</I> if compression was not used.
356     *
357     * @param returnClass <EMBED CLASS='external-html' DATA-FILE-ID=LFEC_RET_CLASS>
358     *
359     * @param <T> This type parameter is simply provided for convenience, to allow the user to
360     * specify the return class, without having to cast the object and suppress warnings, or catch
361     * exceptions.
362     *
363     * @return A de-serialized {@code java.lang.Object} present in the data-file passed by-name
364     * through file-name parameter {@code 'f'}, and cast to the type denoted by parameter
365     * {@code returnClass}.
366     *
367     * @see FileRW#readObjectFromFile(String, boolean)
368     * @see #ERROR_EXIT(Throwable, String)
369     */
370    public static <T> T readObjectFromFile(String f, boolean zip, Class<T> returnClass)
371    {
372        try
373        {
374            Object ret = FileRW.readObjectFromFile(f, zip);
375
376            if (! returnClass.isInstance(ret))
377
378                ERROR_EXIT(
379                    "Serialized Object from file: " + f + "\n" +
380                    "Using expected (" + (zip ? "zip-compression" : "no-compression") + ")\n" +
381                    "Didn't have an object with class-name: [" + returnClass + "]\n" +
382                    "but rather with class-name: [" + ret.getClass().getName() + "]"
383                );
384
385            return returnClass.cast(ret);
386        }
387
388        catch (Throwable t)
389        {
390            ERROR_EXIT(
391                t,
392                "Exception reading Serialized Object from file: " + f + "\n" +
393                "With intended read class-name of: " + returnClass + "\n" +
394                "Using expected (" + (zip ? "zip-compression" : "no-compression") + ")\n"
395            );
396        }
397
398        throw new UnreachableError(); // Should NOT be possible to reach this statement...
399    }
400
401    /**
402     * This loads a {@code java.lang.Object} from a data-file located in a JAR File.
403     * <EMBED CLASS='external-html' DATA-FILE-ID=LFEC_CONTAINER>
404     *
405     * @param classLoaderClass <EMBED CLASS='external-html' DATA-FILE-ID=LFEC_CLASS_LOAD_C>
406     * <EMBED CLASS='external-html' DATA-FILE-ID=RAWTYPES>
407     *
408     * @param f The name of the file containing a serialized {@code java.lang.Object} to load
409     *
410     * @param zip This should be <I>TRUE</I> if, when serialized, the {@code Object} was compressed
411     * too, and <I>FALSE</I> if compression was not used.
412     *
413     * @param returnClass <EMBED CLASS='external-html' DATA-FILE-ID=LFEC_RET_CLASS> 
414     *
415     * @param <T> This type parameter is simply provided for convenience, to allow the user to
416     * specify the return class, without having to cast the object and suppress warnings, or catch
417     * exceptions.
418     *
419     * @return a de-serialized {@code java.lang.Object} present in the data-file passed by-name
420     * through file-name parameter {@code 'f'}, and cast to the type denoted by parameter
421     * {@code 'returnClass'}.
422     *
423     * @see #ERROR_EXIT(Throwable, String)
424     */
425    public static <T> T readObjectFromFile_JAR
426        (Class<?> classLoaderClass, String f, boolean zip, Class<T> returnClass)
427    {
428        // These are 'java.lang.AutoCloseable', and java handles them automatically
429        // if there is an exception
430
431        try (
432            InputStream is = classLoaderClass.getResourceAsStream(f);
433
434            // The user may or may not have asked for reading a *COMPRESSED* file
435            ObjectInputStream ois = zip
436                ? new ObjectInputStream(new GZIPInputStream(is))
437                : new ObjectInputStream(is);
438        )
439        {
440            Object ret = ois.readObject();
441
442            if (! returnClass.isInstance(ret)) ERROR_EXIT(
443                "Serialized Object from jar-file loading-using class: " +
444                classLoaderClass.getCanonicalName() + "\n" +
445                "Looking for data-file named: " + f + "\n" +
446                "Using expected (" + (zip ? "zip-compression" : "no-compression") + ")\n" +
447                "Didn't have an object with class-name: [" + returnClass + "]\n" +
448                "but rather with class-name: [" + ret.getClass().getName() + "]"
449            );
450
451            return returnClass.cast(ret);
452        }
453
454        catch (Throwable t)
455        {
456            ERROR_EXIT(
457                t,
458                "Exception reading Serialized Object from jar-file, loading-using class: " +
459                classLoaderClass.getCanonicalName() + "\n" +
460                "Looking for data-file named: " + f + "\n" +
461                "And attempting to retrieve an object having class-name: " + returnClass + "\n" +
462                "Using expected (" + (zip ? "zip-compression" : "no-compression") + ")\n"
463            );
464
465            throw new UnreachableError(); // Should NOT be possible to reach this statement...
466        }
467    }
468
469
470    // ********************************************************************************************
471    // ********************************************************************************************
472    // Google Cloud Server - Public Static Inner Class
473    // ********************************************************************************************
474    // ********************************************************************************************
475
476
477    /**
478     * The Google Cloud Server Storage Bucket extension of "Load File Exception Catch" does the
479     * work that <CODE>LFEC</CODE> does, but for GCS Storage Buckets, rather than operating 
480     * system files.
481     * 
482     * <EMBED CLASS='external-html' DATA-FILE-ID=LFEC_GCSSB>
483     */
484    @Torello.JavaDoc.StaticFunctional
485    public static class GCSSB
486    {
487        private GCSSB() { }
488
489        /**
490         * This will read a Java Serialized {@code java.lang.Object} from a location in a Google
491         * Cloud Server {@code Storage Bucket}.
492         *
493         * @param storage <EMBED CLASS='external-html' DATA-FILE-ID=GCSSB_STORAGE>
494         *
495         * @param bucket The {@code bucket} name of the {@code bucket} from a Google Cloud Server
496         * account.
497         *
498         * @param completeFileName <EMBED CLASS='external-html' DATA-FILE-ID=GCSSB_CMPL_FNAME>
499         *
500         * @param zip <EMBED CLASS='external-html' DATA-FILE-ID=GCSSB_ZIP>
501         *
502         * @param returnClass This is the type expected to be found by Java in the Serialized
503         * {@code Object} Data-File.  If an {@code Object} is read from this location, but it does
504         * not have the type indicated by this parameter, the program will also halt, and an
505         * explanatory exception message will be printed to the console/terminal.
506         *
507         * @param <T> This type parameter is simply provided for convenience, to allow the user
508         * to specify the return class, without having to cast the object and suppress warnings,
509         * or catch exceptions.
510         *
511         * @return A de-serialized java {@code java.lang.Object} that has been read from a GCS
512         * {@code Storage Bucket}, and cast to the type denoted by parameter
513         * {@code 'returnClass'}.
514         *
515         * @see FileRW#readObjectFromFile(String, boolean)
516         * @see FileRW#readObjectFromFileNOCNFE(String, boolean)
517         * @see #readObjectFromFile(String, boolean, Class)
518         * @see #ERROR_EXIT(String)
519         */
520        public static <T> T readObjectFromFile(
521                Storage storage, String bucket, String completeFileName,
522                boolean zip, Class<T> returnClass
523            )
524        {
525            try
526            {
527                // Read Storage Bucket Data into a byte[] array
528                byte[] bArr = storage.get(bucket, completeFileName).getContent();
529
530                // Build an Input Stream, using that byte[] array as input
531                ByteArrayInputStream bis = new ByteArrayInputStream(bArr);
532
533                // Build an Object Input Stream, using the byte-array input-stream as input
534                ObjectInputStream ois = zip
535                    ? new ObjectInputStream(new GZIPInputStream(bis))
536                    : new ObjectInputStream(bis);
537
538                // Use Java's Object Serialization method to read the Object
539                Object ret = ois.readObject();
540
541                if (! returnClass.isInstance(ret)) ERROR_EXIT(
542                    "Serialized Object read from GCS Storage Bucket: " + bucket + "\n" +
543                    "And file-name: " + completeFileName + "\n" +
544                    "Using expected (" + (zip ? "zip-compression" : "no-compression") + ")\n" +
545                    "Didn't have an object with class-name: " + returnClass + "\n" +
546                    "But rather with className: " + ret.getClass().getName()
547                );
548
549                ois.close(); bis.close();
550
551                return returnClass.cast(ret);
552            }
553
554            catch (Throwable t)
555            {
556                ERROR_EXIT(
557                    t,
558                    "Serialized Object read from GCS Storage Bucket: " + bucket + "\n" +
559                    "And file-name: " + completeFileName + "\n" +
560                    "Using expected (" + (zip ? "zip-compression" : "no-compression") + ")\n" +
561                    "And Expected class-name: " + returnClass + "\n"
562                );
563
564                throw new UnreachableError(); // Cannot reach this statement
565            }
566        }
567
568        /**
569         * This merely loads a text-file from Google's Storage Bucket infrastructure into a
570         * {@code String.}  Make sure to check that the file you are loading does indeed have
571         * text-content.
572         *
573         * @param storage <EMBED CLASS='external-html' DATA-FILE-ID=GCSSB_STORAGE>
574         *
575         * @param bucket The {@code bucket} name of the {@code bucket} from a Google Cloud Server
576         * account.
577         *
578         * @param completeFileName <EMBED CLASS='external-html' DATA-FILE-ID=GCSSB_CMPL_FNAME>
579         *
580         * @return The text file on Google Cloud Server's Storage Bucket file/directory returned as
581         * a {@code java.lang.String}
582         */
583        public static String loadFileToString
584            (Storage storage, String bucket, String completeFileName)
585        { return new String(storage.get(bucket, completeFileName).getContent()); }
586
587        /**
588         * This merely loads a text-file from Google's Storage Bucket infrastructure into a
589         * {@code String}.  Make sure to check that the file you are loading does indeed have
590         * text-content.
591         *
592         * @param storage <EMBED CLASS='external-html' DATA-FILE-ID=GCSSB_STORAGE>
593         *
594         * @param bucket The {@code bucket} name of the {@code bucket} from a Google Cloud Server
595         * account.
596         *
597         * @param completeFileName <EMBED CLASS='external-html' DATA-FILE-ID=GCSSB_CMPL_FNAME>
598         *
599         * @param includeNewLine This tells the method to include, or not-include, a {@code '\n'}
600         * (newline) character to each {@code String}.
601         *
602         * @return The text file on Google Cloud Server's {@code Storage Bucket} file/directory
603         * stuff as a {@code Vector} of {@code String's}.
604         *
605         * @see #loadFileToString(Storage, String, String)
606         */
607        public static Vector<String> loadFileToVector
608            (Storage storage, String bucket, String completeFileName, boolean includeNewLine)
609        {
610            String          s   = loadFileToString(storage, bucket, completeFileName);
611            Vector<String>  ret = new Vector<>();
612
613            int pos     = 0;
614            int delta   = includeNewLine ? 1 : 0;
615            int lastPos = 0;
616
617            while ((pos = s.indexOf('\n')) != -1)
618            {
619                ret.add(s.substring(lastPos, pos + delta));
620                lastPos = pos + 1;
621            }
622
623            if (lastPos < s.length()) ret.add(s.substring(lastPos));
624
625            return ret;
626        }
627    
628        /**
629         * This will write the contents of a java {@code 'CharSequence'}  - includes
630         * {@code String, StringBuffer & StringBuilder} to a file on Google Cloud Server's
631         * storage bucket system.
632         *
633         * @param storage <EMBED CLASS='external-html' DATA-FILE-ID=GCSSB_STORAGE>
634         *
635         * @param bucket The {@code bucket} name of the {@code bucket} from a Google Cloud Server
636         * account.
637         * 
638         * @param completeFileName <EMBED CLASS='external-html' DATA-FILE-ID=GCSSB_CMPL_FNAME>
639         *
640         * @param ASCIIorUTF8  When writing java {@code String's} the file-system, it is generally
641         * not to important to worry about whether java has stored an {@code 'ASCII'} encoded
642         * {@code String}, or a {@code String} encoded using {@code 'UTF-8'}.  Most
643         * foreign-language news-sites require the latter ({@code 'UTF-8'}), but any site that is
644         * strictly English can get by with plain old ASCII.
645         *
646         * <BR /><BR /><DIV CLASS=JDHint>
647         * <B STYLE="color: red">Important:</B> When this boolean is
648         * {@code TRUE}, this method will attempt to presume the character-sequence you have passed
649         * is in ASCII, and write it that way.  When this boolean is set to {@code FALSE},
650         * this method will attempt to write the {@code String} of {@code byte's} as a
651         * {@code 'UTF-8'} encoded character-set.
652         * </DIV>
653         *
654         * <BR /><DIV CLASS=JDHintAlt>
655         * <B STYLE="color: red">Also:</B> I have not made any allowance for Unicode or Unicode
656         * little endian, because I have never used them with either the Chinese or Spanish sites I
657         * scrape.  UTF-8 has been the only other character set I encounter.
658         * </DIV>
659         */
660        public static void writeFile(
661                CharSequence fileAsStr, Storage storage, String bucket,
662                String completeFileName, boolean ASCIIorUTF8
663            )
664        {
665            BlobInfo blobInfo = BlobInfo.newBuilder
666                (BlobId.of(bucket, completeFileName)).setContentType("text/plain").build();
667
668            byte[] file = ASCIIorUTF8
669                ? fileAsStr.toString().getBytes()
670                : fileAsStr.toString().getBytes(java.nio.charset.Charset.forName("UTF-8"));
671
672            Blob blob = storage.create(blobInfo, file);
673        }
674
675        /**
676         * This will write a Java {@code Serializable Object} to a location in a Google Cloud
677         * Server Storage Bucket.
678         * 
679         * @param storage <EMBED CLASS='external-html' DATA-FILE-ID=GCSSB_STORAGE>
680         * 
681         * @param o This may be any {@code Serializable Java Object}.  {@code Serializable Java
682         * Objects} are ones which implement the {@code interface java.io.Serializable}.
683         * 
684         * @param bucket The {@code bucket} name of the {@code bucket} from a Google Cloud Server
685         * account.
686         * 
687         * @param completeFileName <EMBED CLASS='external-html' DATA-FILE-ID=GCSSB_CMPL_FNAME>
688         * 
689         * @param zip <EMBED CLASS='external-html' DATA-FILE-ID=GCSSB_ZIP>
690         */
691        public static void writeObjectToFile(
692                Object o, Storage storage, String bucket,
693                String completeFileName, boolean zip
694            )
695            throws IOException
696        {
697            // Retrieves a file-name object using a GCS BUCKET-NAME, and the FILE-NAME (in
698            // the bucket)            
699
700            BlobId blobId = BlobId.of(bucket, completeFileName);
701
702
703            // This BlobInfo is GCS version of "java.io.File".  It points to a specific file
704            // inside a GCS Bucket (which was specified earlier)
705
706            BlobInfo blobInfo = BlobInfo
707                .newBuilder(blobId)
708                // .setContentType("text/plain")
709                .build();
710
711            // This will save the Serialized Object Data to a Stream (and eventually an array)
712            ByteArrayOutputStream baos = new ByteArrayOutputStream();
713
714            // This stream writes serialized Java-Objects to the Storage Bucket
715            ObjectOutputStream oos = zip
716                ? new ObjectOutputStream(new GZIPOutputStream(baos))
717                : new ObjectOutputStream(baos);
718
719            oos.writeObject(o);
720
721            oos.flush(); baos.flush(); oos.close();
722
723            // Convert that BAOS to a Byte-Array
724            byte[]  bArr = baos.toByteArray();
725
726
727            // Write the BYTE-ARRAY to the GCS Bucket and file using the "BlobInfo" that was built
728            // a few lines ago.
729
730            Blob blob = storage.create(blobInfo, bArr);
731        }
732    }
733}