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