001package Torello.Java;
002
003import java.util.*;
004import java.util.regex.*;
005import java.util.zip.*;
006import java.io.*;
007import java.util.stream.*;
008import java.util.function.*;
009
010import java.text.DecimalFormat;
011import java.net.URL;
012
013import Torello.Java.Function.IntCharFunction;
014import Torello.Java.Function.IntTFunction;
015
016import Torello.Java.ReadOnly.ReadOnlyList;
017import Torello.Java.ReadOnly.ReadOnlyArrayList;
018
019import Torello.Java.Additional.Counter;
020
021/**
022 * A plethora of extensions to Java's {@code String} class.
023 * 
024 * <EMBED CLASS='external-html' DATA-FILE-ID=STRING_PARSE>
025 */
026@Torello.JavaDoc.StaticFunctional
027public class StringParse
028{
029    private StringParse() { }
030
031
032    // ********************************************************************************************
033    // ********************************************************************************************
034    // Constants
035    // ********************************************************************************************
036    // ********************************************************************************************
037
038
039    private static final DecimalFormat formatter = new DecimalFormat("#,###");
040
041    /**
042     * This regular expression simply matches white-space found in a java {@code String}.
043     * @see #removeWhiteSpace(String)
044     */
045    public static final Pattern WHITE_SPACE_REGEX = Pattern.compile("\\s+");
046
047    /**
048     * This regular expression simply matches the comma.  The only reason for including this here
049     * is because the java {@code class 'Pattern'} contains a method called
050     * {@code Stream<String> 'splitAsStream(CharSequence)'} which is used for the CSV method
051     * further below
052     * 
053     * @see StrCSV#CSV(String, boolean, boolean)
054     * @see FileRW#readDoublesFromFile(String, boolean, boolean)
055     * @see FileRW#readLongsFromFile(String, boolean, boolean, int)
056     */
057    public static final Pattern COMMA_REGEX = Pattern.compile(",");
058
059    /**
060     * This regular expression is used for integer and floating-point numbers that use the
061     * comma ({@code ','}) between the digits that comprise the number.  For example, this
062     * Regular Expression would match the {@code String} {@code "900,800,75.00"}.
063     * 
064     * @see FileRW#readIntsFromFile(String, boolean, boolean, int)
065     */
066    public static final Pattern NUMBER_COMMMA_REGEX = Pattern.compile("(\\d),(\\d)");
067
068    /**
069     * This represents any version of the new-line character.  Note that the {@code '\r\n'} version
070     * comes before the single {@code '\r'} version in the regular-expression, to guarantee that
071     * if both are present, they are treated as a single newline.
072     */
073    public static final Pattern NEWLINEP = Pattern.compile("\\r\\n|\\r|\\n");
074
075    /**
076     * Predicate for new-line characters
077     * @see #NEWLINEP
078     */
079    public static final Predicate<String> newLinePred = NEWLINEP.asPredicate();
080
081    /** The months of the year, as an immutable list of {@code String's}. */
082    public static final ReadOnlyList<String> months = new ReadOnlyArrayList<>(
083        "January", "February", "March", "April", "May", "June",
084        "July", "August", "September", "October", "November", "December"
085    );
086
087    private static final Calendar internalCalendar = Calendar.getInstance();
088
089    /** This is the list of characters that need to be escaped for a regular expression */
090    public static final String REG_EX_ESCAPE_CHARS = "\\/()[]{}$^+*?-.";
091
092    /** Alpha-Numeric RegEx */
093    public static final Pattern ALPHA_NUMERIC = Pattern.compile("^[\\d\\w]*$");
094
095    /**
096     * Alpha-Numeric {@code String} Predicate.
097     * @see #ALPHA_NUMERIC
098     */
099    public static final Predicate<String> alphaNumPred = ALPHA_NUMERIC.asPredicate();
100
101    // The minimum value for the byte primitive type, without the minus sign.
102    private static final char[] BYTE_MIN_VALUE_DIGITS_AS_CHARS = { '2', '5', '6' };
103
104    // The minimum value for the short primitive type, without the minus sign.
105    private static final char[] SHORT_MIN_VALUE_DIGITS_AS_CHARS = { '6', '5', '5', '3', '6' };
106
107    // The minimum value for the int primitive type, without the minus sign.
108    private static final char[] INT_MIN_VALUE_DIGITS_AS_CHARS =
109    { '2', '1', '4', '7', '4', '8', '3', '6', '4', '8' };
110
111    // The minimum value for the long primitive type, without the minus sign.
112    private static final char[] LONG_MIN_VALUE_DIGITS_AS_CHARS =
113    {
114        '2', '1', '4', '9', '2', '2', '3', '3', '7', '2', '0', '3', '6', '8', '5', '4', 
115        '7', '7', '5', '8', '0', '8'
116    };
117
118    /** An empty {@code String} array. */
119    public static final String[] EMPTY_STR_ARRAY = {};
120
121
122    // ********************************************************************************************
123    // ********************************************************************************************
124    // methods
125    // ********************************************************************************************
126    // ********************************************************************************************
127
128
129    /**
130     * Makes a {@code long} number like {@code 123456789} into a number-string such as:
131     * {@code "123,456,789"}. Java's {@code package java.text.*} is easy to use, and versatile, but
132     * the commands are not always so easy to remember.
133     *
134     * @param l Any {@code long} integer.  Comma's will be inserted for every third power of ten
135     * 
136     * @return After calling java's {@code java.text.DecimalFormat} class, a {@code String}
137     * representing this parameter will be returned.
138     */
139    public static String commas(long l)
140    { return formatter.format(l); }
141
142    /**
143     * Trims any white-space {@code Characters} from the end of a {@code String}.
144     * 
145     * <BR /><TABLE CLASS=JDBriefTable>
146     * <TR><TH>Input String:</TH><TH>Output String:</TH></TR>
147     * <TR><TD>{@code "A Quick Brown Fox\n \t"}</TD><TD>{@code "A Quick Brown Fox"}</TD></TR>
148     * <TR><TD>{@code "\tA Lazy Dog."}</TD><TD>{@code "\tA Lazy Dog."}</TD></TR>
149     * <TR><TD>{@code "   "  (only white-space)}</TD><TD>{@code ""}</TD></TR>
150     * <TR><TD>{@code "" (empty-string)}</TD><TD>{@code ""}</TD></TR>
151     * <TR><TD>{@code null}</TD><TD>throws {@code NullPointerException}</TD></TR>
152     * </TABLE>
153     * 
154     * @param s Any Java {@code String}
155     * 
156     * @return A copy of the same {@code String} - <I>but all characters that matched Java
157     * method {@code java.lang.Character.isWhitespace(char)}</I> and were at the end of the 
158     * {@code String} will not be included in the returned {@code String}.
159     * 
160     * <BR /><BR />If the {@code zero-length String} is passed to parameter {@code 's'}, it
161     * shall be returned immediately.
162     * 
163     * <BR /><BR />If the resultant-{@code String} has zero-length, it is returned, without
164     * exception.
165     */
166    public static String trimRight(String s)
167    {
168        if (s.length() == 0) return s;
169
170        int pos = s.length();
171
172        while ((pos > 0) && Character.isWhitespace(s.charAt(--pos)));
173
174        if (pos == 0) if (Character.isWhitespace(s.charAt(0))) return "";
175
176        return s.substring(0, pos + 1);
177    }
178
179    /**
180     * Trims any white-space {@code Characters} from the beginning of a {@code String}.
181     * 
182     * <TABLE CLASS=JDBriefTable>
183     * <TR><TH>Input String:</TH><TH>Output String:</TH></TR>
184     * <TR><TD>{@code "\t  A Quick Brown Fox"}</TD><TD>{@code "A Quick Brown Fox"}</TD></TR>
185     * <TR><TD>{@code "A Lazy Dog. \n\r\t"}</TD><TD>{@code "A Lazy Dog. \n\r\t"}</TD></TR>
186     * <TR><TD>{@code "   "  (only white-space)}</TD><TD>{@code ""}</TD></TR>
187     * <TR><TD>{@code ""  (empty-string)}</TD><TD>{@code ""}</TD></TR>
188     * <TR><TD>{@code null}</TD><TD>throws {@code NullPointerException}</TD></TR>
189     * </TABLE>
190     * 
191     * @param s Any Java {@code String}
192     * 
193     * @return A copy of the same {@code String} - <I>but all characters that matched Java
194     * method {@code java.lang.Character.isWhitespace(char)}</I> and were at the start of the 
195     * {@code String} will not be included in the returned {@code String}.
196     * 
197     * <BR /><BR />If the {@code zero-length String} is passed to parameter {@code 's'}, it
198     * shall be returned immediately.
199     * 
200     * <BR /><BR />If the resultant-{@code String} has zero-length, it is returned, without
201     * exception.
202     */
203    public static String trimLeft(String s)
204    {
205        int pos = 0;
206        int len = s.length();
207
208        if (len == 0) return s;
209
210        while ((pos < len) && Character.isWhitespace(s.charAt(pos++)));
211
212        if (pos == len) if (Character.isWhitespace(s.charAt(len-1))) return "";
213
214        return s.substring(pos - 1);
215    }
216
217    /**
218     * Primarily for convenience in remembering earlier C-style {@code printf(...)} formatting
219     * commands.
220     * 
221     * <BR /><BR />This method will "left pad" an input {@code String} with spaces, if
222     * {@code s.length() < totalStrLength}. If input-parameter {@code 's'} is equal-to or
223     * longer-than the value in {@code 'totalStringLength'}, then the original {@code String} shall
224     * be returned.
225     * 
226     * <TABLE CLASS=JDBriefTable>
227     * <TR><TH>Input Parameters</TH><TH>Returned String</TH></TR>
228     * <TR><TD>{@code "Quick Brown Fox"}<BR />{@code 20}</TD>
229     *     <TD><PRE>{@code "     Quick Brown Fox"}</PRE></TD>
230     * </TR>
231     * <TR><TD>{@code "Hello World"}<BR />{@code 15}</TD>
232     *     <TD><PRE>{@code "    Hello World"}</PRE></TD>
233     * </TR>
234     * <TR><TD>{@code "Write Once, Run Anywhere"}<BR />{@code 10}</TD>
235     *     <TD>{@code "Write Once, Run Anywhere"}</TD>
236     * </TR>
237     * <TR><TD>{@code null}</TD><TD>{@code NullPointerException}</TD></TR>
238     * </TABLE>
239     * 
240     * @param s This may be any {@code java.lang.String}
241     * 
242     * @param totalStringLength If {@code s.length()} is smaller than {@code 'totalStringLength'},
243     * then as many space characters ({@code ' '}) as are needed to ensure that the returned
244     * {@code 'String'} has length equal to {@code 'totalStringLength'} will be
245     * <B><I>prepended</B></I> to the input {@code String} parameter {@code 's'}.
246     * 
247     * <BR /><BR />If {@code s.length()} is greater than {@code 'totalStringLength'}, then the
248     * original input shall be returned.
249     * 
250     * @throws IllegalArgumentException If {@code totalStringLength} is zero or negative.
251     * 
252     * @see #rightSpacePad(String, int)
253     */
254    public static String leftSpacePad(String s, int totalStringLength)
255    {
256        if (totalStringLength <= 0) throw new IllegalArgumentException(
257            "totalString length was '" + totalStringLength + ", " +
258            "however it is expected to be a positive integer."
259        );
260
261        return (s.length() >= totalStringLength) 
262            ? s 
263            : String.format("%1$" + totalStringLength + "s", s);
264    }
265
266    /**
267     * Primarily for convenience in remembering earlier C-style {@code printf(...)} formatting
268     * commands.
269     * 
270     * <BR /><BR />This method will "right pad" an input {@code String} with spaces, if
271     * {@code s.length() < totalStrLength}. If input-parameter {@code 's'} is equal-to or
272     * longer-than the value in {@code 'totalStringLength'}, then the original {@code String} shall
273     * be returned.
274     * 
275     * <TABLE CLASS=JDBriefTable>
276     * <TR><TH>Input Parameters</TH><TH>Returned String</TH></TR>
277     * <TR><TD>{@code "Quick Brown Fox"}<BR />{@code 20}</TD>
278     *     <TD><PRE>{@code "Quick Brown Fox     "}</PRE></TD>
279     * </TR>
280     * <TR><TD>{@code "Hello World"}<BR />{@code 15}</TD>
281     *     <TD><PRE>{@code "Hello World    "}</PRE></TD>
282     * </TR>
283     * <TR><TD>{@code "Write Once, Run Anywhere"}<BR />{@code 10}</TD>
284     *     <TD>{@code "Write Once, Run Anywhere"}</TD>
285     * </TR>
286     * <TR><TD>{@code null}</TD><TD>{@code NullPointerException}</TD></TR>
287     * </TABLE>
288     * 
289     * @param s This may be any {@code java.lang.String}
290     * 
291     * @param totalStringLength If {@code s.length()} is smaller than {@code 'totalStringLength'},
292     * then as many space characters ({@code ' '}) as are needed to ensure that the returned
293     * {@code 'String'} has length equal to {@code 'totalStringLength'} will be
294     * <B><I>postpended</B></I> to the input {@code String} parameter {@code 's'}.
295     * 
296     * <BR /><BR />If {@code s.length()} is greater than {@code 'totalStringLength'}, then the
297     * original input shall be returned.
298     * 
299     * @throws IllegalArgumentException If {@code totalStringLength} is zero or negative.
300     * 
301     * @see #leftSpacePad(String, int)
302     */
303    public static String rightSpacePad(String s, int totalStringLength)
304    {
305        if (totalStringLength <= 0) throw new IllegalArgumentException(
306            "totalString length was '" + totalStringLength + "', " +
307            "however it is expected to be a positive integer."
308        );
309
310        return (s.length() >= totalStringLength) 
311            ? s 
312            : String.format("%1$-" + totalStringLength + "s", s);
313    }
314
315    /**
316     * Runs a Regular-Expression over a {@code String} to retrieve all matches that occur between
317     * input {@code String} parameter {@code 's'} and Regular-Expression {@code 'regEx'}.
318     * 
319     * @param s Any Java {@code String}
320     * @param regEx Any Java Regular-Expression
321     * 
322     * @param eliminateOverlappingMatches When this parameter is passed {@code 'TRUE'}, successive
323     * matches that have portions which overlap each-other are eliminated.
324     * 
325     * @return An array of all {@code MatchResult's} (from package {@code 'java.util.regex.*'}) that
326     * were produced by iterating the {@code Matcher's} {@code 'find()'} method.
327     */
328    public static MatchResult[] getAllMatches
329        (String s, Pattern regEx, boolean eliminateOverlappingMatches)
330    {
331        Stream.Builder<MatchResult> b       = Stream.builder();
332        Matcher                     m       = regEx.matcher(s);
333        int                         prevEnd = 0;
334
335        while (m.find())
336        {
337            MatchResult matchResult = m.toMatchResult();
338
339            // This skip any / all overlapping matches - if the user has requested it
340            if (eliminateOverlappingMatches) if (matchResult.start() < prevEnd) continue;
341
342            b.accept(matchResult);
343
344            prevEnd = matchResult.end();
345        }
346
347        // Convert the Java-Stream into a Java-Array and return the result
348        return b.build().toArray(MatchResult[]::new);
349    }
350
351
352    // ********************************************************************************************
353    // ********************************************************************************************
354    // Helper set & get for strings
355    // ********************************************************************************************
356    // ********************************************************************************************
357
358
359    /**
360     * This sets a character in a {@code String} to a new value, and returns a result
361     * @param str Any java {@code String}
362     * @param i An index into the underlying character array of that {@code String}.
363     * @param c A new character to be placed at the <I>i'th position</I> of this {@code String}.
364     * 
365     * @return a new java {@code String}, with the appropriate index into the {@code String}
366     * substituted using character parameter {@code 'c'}.
367     */
368    public static String setChar(String str, int i, char c)
369    {
370        return ((i + 1) < str.length())
371            ? (str.substring(0, i) + c + str.substring(i + 1))
372            : (str.substring(0, i) + c);
373    }
374
375    /**
376     * This removes a character from a {@code String}, and returns a new {@code String} as a
377     * result.
378     * 
379     * @param str Any Java-{@code String}.
380     * 
381     * @param i This is the index into the underlying java {@code char}-array whose character will
382     * be removed from the return {@code String}.
383     * 
384     * @return Since Java {@code String}'s are all immutable, this {@code String} that is returned
385     * is completely new, with the character that was originally at index 'i' removed.
386     */
387    public static String delChar(String str, int i)
388    {
389        if ((i + 1) < str.length())
390            return str.substring(0, i) + str.substring(i + 1);
391        else
392            return str.substring(0, i);
393    }
394
395    /**
396     * Returns the same {@code String} is input, but trims all spaces down to a single space.
397     * Each and every <I>lone / independent or contiguous</I> white-space character is reduced
398     * to a single space-character.
399     * 
400     * <TABLE CLASS=JDBriefTable>
401     * <TR><TH>Input String</TH><TH>Output String</TH></TR>
402     * <TR><TD><PRE>{@code "This   has   extra   spaces\n"}</PRE></TD>
403     *     <TD>{@code "This has extra spaces "}</TD>
404     * </TR>
405     * <TR><TD>{@code "This does not"}</TD>
406     *     <TD>{@code "This does not"}</TD>
407     * </TR>
408     * <TR><TD>{@code "\tThis\nhas\ttabs\nand\tnewlines\n"}</TD>
409     *     <TD>{@code " This has tabs and newlines "}</TD>
410     * </TR>
411     * </TABLE>
412     *
413     * @param s Any Java {@code String}
414     * 
415     * @return A {@code String} where all white-space is compacted to a single space.  This is
416     * generally how HTML works, when it is displayed in a browser.
417     */
418    public static String removeDuplicateSpaces(String s)
419    { return StringParse.WHITE_SPACE_REGEX.matcher(s).replaceAll(" "); }
420
421    /**
422     * This string-modify method simply removes any and all white-space matches found within a
423     * java-{@code String}.
424     * 
425     * <TABLE CLASS=JDBriefTable>
426     * <TR><TH>Input String</TH><TH>Output String</TH></TR>
427     * <TR><TD><PRE>{@code "This   Has   Extra   Spaces\n"}</PRE></TD>
428     *     <TD>{@code "ThisHasExtraSpaces"}</TD>
429     * </TR>
430     * <TR><TD>{@code "This Does Not"}</TD>
431     *     <TD>{@code "ThisDoesNot"}</TD>
432     * </TR>
433     * <TR><TD>{@code "\tThis\nHas\tTabs\nAnd\tNewlines\n"}</TD>
434     *     <TD>{@code "ThisHasTabsAndNewlines"}</TD>
435     * </TR>
436     * </TABLE>
437     * 
438     * @param s Any {@code String}, but if it has any white-space (space that matches
439     * regular-expression: {@code \w+}) then those character-blocks will be removed
440     * 
441     * @return A new {@code String} without any {@code \w} (RegEx for 'whitespace')
442     * 
443     * @see #WHITE_SPACE_REGEX
444     */
445    public static String removeWhiteSpace(String s)
446    { return WHITE_SPACE_REGEX.matcher(s).replaceAll(""); }
447
448    /**
449     * Generates a {@code String} that contains {@code n} copies of character {@code c}.
450     * @return {@code n} copies of {@code c}, as a {@code String}.
451     * @throws IllegalArgumentException If the value passed to parameter {@code 'n'} is negative
452     * @see StrSource#caretBeneath(String, int)
453     */
454    public static String nChars(char c, int n)
455    {
456        if (n < 0) throw new IllegalArgumentException("Value of parameter 'n' is negative: " + n);
457
458        char[] cArr = new char[n];
459        Arrays.fill(cArr, c);
460        return new String(cArr);
461    }
462
463    /**
464     * Generates a {@code String} that contains {@code n} copies of {@code s}.
465     * @return {@code n} copies of {@code s} as a {@code String}.
466     * @throws NException if the value provided to parameter {@code 'n'} is negative.
467     */
468    public static String nStrings(String s, int n)
469    {
470        if (n < 0) throw new NException("A negative value was passed to 'n' [" + n + ']');
471
472        StringBuilder sb = new StringBuilder();
473
474        for (int i=0; i < n; i++) sb.append(s);
475
476        return sb.toString();
477    }
478
479    /**
480     * This method checks whether or not a java-{@code String} has white-space.
481     * 
482     * @param s Any Java-{@code String}.  If this {@code String} has any white-space, this method
483     * will return {@code TRUE}
484     * 
485     * @return {@code TRUE} If there is any white-space in this method, and {@code FALSE} otherwise.
486     * 
487     * @see #WHITE_SPACE_REGEX
488     */
489    public static boolean hasWhiteSpace(String s)
490    { return WHITE_SPACE_REGEX.matcher(s).find(); }
491
492    /**
493     * Counts the number of instances of character input {@code char c} contained by the
494     * input {@code String s}
495     * 
496     * @param s Any {@code String} containing any combination of ASCII/UniCode characters
497     * 
498     * @param c Any ASCII/UniCode character.
499     * 
500     * @return The number of times {@code char c} occurs in {@code String s}
501     */
502    public static int countCharacters(String s, char c)
503    {
504        int count = 0;
505        int pos   = 0;
506        while ((pos = s.indexOf(c, pos + 1)) != -1) count++;
507        return count;
508    }
509
510
511    /**
512     * If the {@code String} passed to this method contains a single-quote on both sides of the
513     * {@code String}, or if it contains a double-quote on both sides of this {@code String}, then
514     * this method shall return a new {@code String} that is shorter in length by 2, and leaves off
515     * the first and last characters of the input parameter {@code String}.
516     * 
517     * <BR /><BR /><B>HOPEFULLY,</B> The name of this method explains clearly what this method does
518     *
519     * @param s This may be any java {@code String}.  Only {@code String's} whose first and last
520     * characters are not only quotation marks (single or double), but also they are <B>the same,
521     * identical, quotation marks on each side.</B>
522     * 
523     * @return A new {@code String} that whose first and last quotation marks are gone - if they
524     * were there when this method began.
525     */
526    public static String ifQuotesStripQuotes(String s)
527    {
528        if (s == null)      return null;
529        if (s.length() < 2) return s;
530
531        int lenM1 = s.length() - 1; // Position of the last character in the String
532
533        if (    ((s.charAt(0) == '\"')  && (s.charAt(lenM1) == '\"'))       // String has Double-Quotation-Marks
534                                        ||                                  //            ** or ***
535                ((s.charAt(0) == '\'')  && (s.charAt(lenM1) == '\''))  )    // String has Single-Quotation-Marks
536            return s.substring(1, lenM1);
537        else  
538            return s;
539    }
540
541    /**
542     * Counts the number of lines of text inside of a Java {@code String}.
543     * 
544     * @param text This may be any text, as a {@code String}.
545     * 
546     * @return Returns the number of lines of text.  The integer returned shall be precisely
547     * equal to the number of {@code '\n'} characters <B><I>plus one!</I></B>
548     */
549    public static int numLines(String text)
550    {
551        if (text.length() == 0) return 0;
552
553        int pos     = -1;
554        int count   = 0;
555
556        do
557        {
558            pos = text.indexOf('\n', pos + 1);
559            count++;
560        }
561        while (pos != -1);
562
563        return count;
564    }
565
566
567    // ********************************************************************************************
568    // ********************************************************************************************
569    // Misc Date String Functions
570    // ********************************************************************************************
571    // ********************************************************************************************
572
573                                
574    /**
575     * Converts an integer into a Month.  I could just use the class {@code java.util.Calendar},
576     * but it is so complicated, that using an internal list is easier.
577     * 
578     * @param month The month, as a number from {@code '1'} to {@code '12'}.
579     * @return A month as a {@code String} like: {@code "January"} or {@code "August"}
580     * @see #months
581     */
582    public static String monthStr(int month) { return months.get(month); }
583
584    /**
585     * Generates a "Date String" using the character separator {@code '.'}  
586     * @return A {@code String} in the form: {@code YYYY.MM.DD}
587     */
588    public static String dateStr() { return dateStr('.', false); }
589
590    /**
591     * Generates a "Date String" using the <I>separator</I> parameter as the separator between
592     * numbers
593     * 
594     * @param separator Any ASCII or UniCode character.
595     * 
596     * @return A {@code String} of the form: {@code YYYYcMMcDD} where {@code 'c'} is the passed
597     * {@code 'separator'} parameter.
598     */
599    public static String dateStr(char separator) { return dateStr(separator, false); }
600
601    /**
602     * Generates a "Date String" that is consistent with the directory-name file-storage locations
603     * used to store articles from {@code http://Gov.CN}.
604     * 
605     * @return The {@code String's} used for the Chinese Government Web-Portal Translation Pages
606     */
607    public static String dateStrGOVCN() { return dateStr('/', false).replaceFirst("/", "-"); }
608    // "2017-12/05"
609
610    /**
611     * This class is primary included because although Java has a pretty reasonable "general
612     * purpose" calendar class/interface, but a consistent / same {@code String} since is needed
613     * because the primary use here is for building the names of files.
614     *
615     * @param separator Any ASCII or Uni-Code character.
616     * 
617     * @param includeMonthName When <I>TRUE</I>, the English-Name of the month ({@code 'January'}
618     * ... {@code 'December'}) will be appended to the month number in the returned {@code String}.
619     * 
620     * @return The year, month, and day as a {@code String}.
621     */
622    public static String dateStr(char separator, boolean includeMonthName)
623    {
624        Calendar    c   = internalCalendar;
625        String      m   = zeroPad10e2(c.get(Calendar.MONTH) + 1); // January is month zero!
626        String      d   = zeroPad10e2(c.get(Calendar.DAY_OF_MONTH));
627
628        if (includeMonthName) m += " - " + c.getDisplayName(Calendar.MONTH, 2, Locale.US);
629
630        if (separator != 0) return c.get(Calendar.YEAR) + "" + separator + m + separator + d;
631        else                return c.get(Calendar.YEAR) + "" + m + d;
632    }
633
634    /**
635     * Returns a {@code String} that has the year and the month (but not the day, or other time
636     * components).
637     *
638     * @return Returns the current year and month as a {@code String}.
639     */
640    public static String ymDateStr() { return ymDateStr('.', false); }
641
642    /**
643     * Returns a {@code String} that has the year and the month (but not the day, or other time
644     * components).
645     * 
646     * @param separator The single-character separator used between year, month and day.
647     * 
648     * @return The current year and month as a {@code String}.
649     */
650    public static String ymDateStr(char separator) { return ymDateStr(separator, false); }
651
652    /**
653     * Returns a {@code String} that has the year and the month (but not the day, or other time
654     * components).
655     * 
656     * @param separator The single-character separator used between year, month and day.
657     * 
658     * @param includeMonthName When this is true, the name of the month, in English, is included
659     * with the return {@code String}.
660     * 
661     * @return YYYYseparatorMM(? include-month-name)
662     */
663    public static String ymDateStr(char separator, boolean includeMonthName)
664    {
665        Calendar    c   = internalCalendar;
666        String      m   = zeroPad10e2(c.get(Calendar.MONTH) + 1); // January is month zero!
667
668        if (includeMonthName) m += " - " + c.getDisplayName(Calendar.MONTH, 2, Locale.US);
669
670        if (separator != 0) return c.get(Calendar.YEAR) + "" + separator + m;
671        else                return c.get(Calendar.YEAR) + "" + m;
672    }
673
674
675    // ********************************************************************************************
676    // ********************************************************************************************
677    // Misc Time String Functions
678    // ********************************************************************************************
679    // ********************************************************************************************
680
681
682    /**
683     * Returns the current time as a {@code String}.
684     * 
685     * @return military time - with AM|PM (redundant) added too.
686     * Includes only Hour and Minute - separated by a colon character {@code ':'}
687     * 
688     * @see #timeStr(char)
689     */
690    public static String timeStr() { return timeStr(':'); }
691
692    /**
693     * Returns the current time as a {@code String}.
694     * 
695     * @param separator The character used to separate the minute &amp; hour fields
696     * 
697     * @return military time - with AM|PM added redundantly, and a separator of your choosing.
698     */
699    public static String timeStr(char separator)
700    {
701        Calendar    c   = internalCalendar;
702        int         ht  = c.get(Calendar.HOUR) + ((c.get(Calendar.AM_PM) == Calendar.AM) ? 0 : 12);
703        String      h   = zeroPad10e2((ht == 0) ? 12 : ht);  // 12:00 is represented as "0"... changes this...
704        String      m   = zeroPad10e2(c.get(Calendar.MINUTE));
705        String      p   = (c.get(Calendar.AM_PM) == Calendar.AM) ? "AM" : "PM";
706
707        if (separator != 0) return h + separator + m + separator + p;
708        else                return h + m + p;
709    }
710
711    /**
712     * Returns the current time as a {@code String}.  This method uses all time components
713     * available.
714     * 
715     * @return military time - with AM|PM added redundantly.
716     */
717    public static String timeStrComplete()
718    {
719        Calendar    c   = internalCalendar;
720        int         ht  = c.get(Calendar.HOUR) + ((c.get(Calendar.AM_PM) == Calendar.AM) ? 0 : 12);
721        String      h   = zeroPad10e2((ht == 0) ? 12 : ht);  // 12:00 is represented as "0"
722        String      m   = zeroPad10e2(c.get(Calendar.MINUTE));
723        String      s   = zeroPad10e2(c.get(Calendar.SECOND));
724        String      ms  = zeroPad(c.get(Calendar.MILLISECOND));
725        String      p   = (c.get(Calendar.AM_PM) == Calendar.AM) ? "AM" : "PM";
726
727        return h + '-' + m + '-' + p + '-' + s + '-' + ms + "ms";
728    }
729
730    /**
731     * The words "ordinal indicator" are referring to the little character {@code String} that is
732     * often used in English to make a number seem more a part of an english sentence.
733     * 
734     * @param i Any positive integer (greater than 0)
735     *
736     * @return This will return the following strings:
737     * 
738     * <TABLE CLASS=JDBriefTable>
739     * <TR><TH>Input:   </TH><TH>RETURNS:</TH></TR>
740     * <TR><TD>i = 1    </TD><TD>"st" &nbsp;(as in "1st","first")   </TD></TR>
741     * <TR><TD>i = 2    </TD><TD>"nd" &nbsp;(as in "2nd", "second") </TD></TR>
742     * <TR><TD>i = 4    </TD><TD>"th" &nbsp;(as in "4th")           </TD></TR>
743     * <TR><TD>i = 23   </TD><TD>"rd" &nbsp;(as in "23rd")          </TD></TR>
744     * </TABLE>
745     * 
746     * @throws IllegalArgumentException If i is negative, or zero
747     */
748    public static String ordinalIndicator(int i)
749    {
750        if (i < 1)
751            throw new IllegalArgumentException("i: " + i + "\tshould be a natural number > 0.");
752
753        // Returns the last 2 digits of the number, or the number itself if it is less than 100.
754        // Any number greater than 100 - will not have the "text-ending" (1st, 2nd, 3rd..) affected
755        // by the digits after the first two digits.  Just analyze the two least-significant digits
756        i = i % 100;
757
758        // All numbers between "4th" and "19th" end with "th"
759        if ((i > 3) && (i < 20))    return "th";
760
761        // set i to be the least-significant digit of the number - if that number was 1, 2, or 3
762        i = i % 10;
763
764        // Obvious: English Rules.
765        if (i == 1)                 return "st";
766        if (i == 2)                 return "nd";
767        if (i == 3)                 return "rd";
768
769        // Compiler is complaining.  This statement should never be executed.
770        return "th";
771    }
772
773
774    // ********************************************************************************************
775    // ********************************************************************************************
776    // Zero Padding stuff
777    // ********************************************************************************************
778    // ********************************************************************************************
779
780    
781    /**
782     * This just zero-pads integers with "prepended" zero's.  java.text has all kinds of extremely
783     * intricate zero-padding and text-formatting classes.  However, here, these are generally used
784     * for <B>debug, line-number, or count</B> information that is printed to the UNIX terminal.
785     * When this is the case, a simple and easily remembered <I>'one line method'</I> is a lot more
786     * useful than all of the highly-scalable versions of the text-formatting classes in java.text.
787     * 
788     * @param n Any Integer.  If {@code 'n'} is negative or greater than 1,000 - then null is
789     * returned.
790     * 
791     * @return A zero-padded {@code String} - <B><I>to precisely three orders of 10</I></B>, as in
792     * the example table below:
793     * 
794     * <TABLE CLASS=JDBriefTable>
795     * <TR><TH>Input    </TH><TH><I>RETURNS:</I></TH></TR>
796     * <TR><TD>n = 9    </TD><TD>"009"</TD></TR>
797     * <TR><TD>n = 99   </TD><TD>"099"</TD></TR>
798     * <TR><TD>n = 999  </TD><TD>"999"</TD></TR>
799     * <TR><TD>n = 9999 </TD><TD>null</TD></TR>
800     * <TR><TD>n = -10  </TD><TD>null</TD></TR>
801     * </TABLE>
802     * 
803     * @see #zeroPad10e2(int)
804     * @see #zeroPad10e4(int)
805     */
806    public static String zeroPad(int n)
807    {
808        if (n < 0)      return null;
809        if (n < 10)     return "00" + n;
810        if (n < 100)    return "0" + n;
811        if (n < 1000)   return "" + n;
812        return null;
813    }
814
815    /**
816     * Pads an integer such that it contains enough leading zero's to ensure a String-length of
817     * two.
818     * 
819     * @param n Must be an integer between 0 and 99, or else null will be returned
820     * 
821     * @return A zero-padded String of the integer, <B><I>to precisely two orders of
822     * 10</I></B><BR />. Null is returned if the number cannot fit within two spaces.  Example
823     * table follows:
824     * 
825     * <TABLE CLASS=JDBriefTable>
826     * <TR><TH>Input       </TH><TH><I>RETURNS:</I></TH></TR>
827     * <TR><TD>n = 9       </TD><TD>"09"</TD></TR>
828     * <TR><TD>n = 99      </TD><TD>"99"</TD></TR>
829     * <TR><TD>n = 999     </TD><TD>null</TD></TR>
830     * <TR><TD>n = -10     </TD><TD>null</TD></TR>
831     * </TABLE>
832     * 
833     * @see #zeroPad(int)
834     */
835    public static String zeroPad10e2(int n)
836    {
837        if (n < 0)      return null;
838        if (n < 10)     return "0" + n;
839        if (n < 100)    return "" + n;
840        return null;
841    }
842
843    /**
844     * Pads an integer such that it contains enough leading zero's to ensure a String-length of
845     * four.
846     * 
847     * @param n Must be an integer between 0 and 9999, or else null will be returned
848     * 
849     * @return A zero-padded String of the integer, <B><I>to precisely four orders of 10</I></B>.
850     * Null is returned if the number cannot fit within four spaces.  Example table follows: 
851     * 
852     * <TABLE CLASS=JDBriefTable>
853     * <TR><TH>Input       </TH><TH><I>RETURNS:</I></TH></TR>
854     * <TR><TD>n = 9       </TD><TD>"0009"</TD></TR>
855     * <TR><TD>n = 99      </TD><TD>"0099"</TD></TR>
856     * <TR><TD>n = 999     </TD><TD>"0999"</TD></TR>
857     * <TR><TD>n = 9999    </TD><TD>"9999" </TD></TR>
858     * <TR><TD>n = 99999   </TD><TD>null</TD></TR>
859     * <TR><TD>n = -10     </TD><TD>null</TD></TR>
860     * </TABLE>
861     * 
862     * @see #zeroPad(int)
863     */
864    public static String zeroPad10e4(int n)
865    {
866        if (n < 0)      return null;
867        if (n < 10)     return "000" + n;
868        if (n < 100)    return "00" + n;
869        if (n < 1000)   return "0" + n;
870        if (n < 10000)  return "" + n;
871        return null;
872    }
873
874    /**
875     * Pad's an integer with leading zeroes into a {@code String}.  The number of zeroes padded is 
876     * equal to parameter {@code 'powerOf10'}.  If {@code int 'powerOf10'} were equal to zero, then
877     * any integer passed to this function would return a {@code String} that was precisely three
878     * characters long.  If the value of parameter {@code int 'n'} were larger than {@code 1,000}
879     * or negative, then null would be returned.
880     * 
881     * @param n Must be an integer between {@code '0'} and {@code '9999'} where the number of 
882     * {@code '9'} digits is equal to the value of parameter {@code int 'powerOf10'}
883     * 
884     * @param powerOf10 This must be a positive integer greater than {@code '1'}.  It may not be 
885     * larger {@code '11'}.  The largest value that any integer in Java may attain is
886     * {@code '2,147,483, 647'}
887     * 
888     * @return A zero padded {@code String}.  If a negative number is passed to parameter
889     * {@code 'n'}, then 'null' shall be returned.  Null shall also be returned if the "Power of 10
890     * Exponent of parameter {@code n}" is greater than the integer-value of parameter
891     * {@code 'powerOf10'}
892     *
893     * <BR /><BR /><B>FOR INSTANCE:</B> a call to: {@code zeroPad(54321, 4);} would return null
894     * since the value of parameter {@code 'n'} has five-decimal-places, but {@code 'powerOf10'} is
895     * only 4!
896     * 
897     * @throws IllegalArgumentException if the value parameter {@code 'powerOf10'} is less than 2,
898     * or greater than {@code 11}.
899     */
900    public static String zeroPad(int n, int powerOf10)
901    {
902        if (n < 0) return null;                 // Negative Values of 'n' not allowed
903
904        char[]  cArr    = new char[powerOf10];  // The String's length will be equal to 'powerOf10'
905        String  s       = "" + n;               //       (or else 'null' would be returned)
906        int     i       = powerOf10 - 1;        // Internal Loop variable
907        int     j       = s.length() - 1;       // Internal Loop variable
908
909        Arrays.fill(cArr, '0');                 // Initially, fill the output char-array with all
910                                                // zeros
911
912        while ((i >= 0) && (j >= 0))            // Now start filling that char array with the
913            cArr[i--] = s.charAt(j--);          // actual number
914
915        if (j >= 0) return null;                // if all of parameter 'n' was inserted into the
916                                                // output (number 'n' didn't fit) then powerOf10
917                                                // was insufficient, so return null.
918
919        return new String(cArr);
920    }
921
922
923    // ********************************************************************************************
924    // ********************************************************************************************
925    // Find / Front Last-Front-Slash
926    // ********************************************************************************************
927    // ********************************************************************************************
928
929
930    /**
931     * This function finds the position of the last "front-slash" character {@code '/'} in a
932     * java-{@code String}
933     * 
934     * @param urlOrDir This is any java-{@code String}, but preferably one that is a
935     * {@code URL}, or directory.
936     * 
937     * @return The {@code String}-index of the last 'front-slash' {@code '/'} position in a
938     * {@code String}, or {@code -1} if there are not front-slashes.
939     */
940    public static int findLastFrontSlashPos(String urlOrDir)
941    { return urlOrDir.lastIndexOf('/'); }
942
943    /**
944     * This returns the contents of a {@code String}, after the last front-slash found.
945     * 
946     * <BR /><BR /><B>NOTE:</B> If not front-slash {@code '/'} character is found, then the
947     * original {@code String} is returned.
948     *
949     * @param urlOrDir This is any java-{@code String}, but preferably one that is a
950     * {@code URL}, or directory.
951     * 
952     * @return the portion of the {@code String} after the final front-slash {@code '/'} character.
953     * If there are no front-slash characters found in this {@code String}, then the original
954     * {@code String} shall be returned.
955     */
956    public static String fromLastFrontSlashPos(String urlOrDir)
957    {
958        int pos = urlOrDir.lastIndexOf('/');
959        if (pos == -1) return urlOrDir;
960        return urlOrDir.substring(pos + 1);
961    }
962
963    /**
964     * This returns the contents of a {@code String}, before the last front-slash found (including
965     * the front-slash {@code '/'} itself).
966     * 
967     * <BR /><BR /><B>NOTE:</B> If no front-slash {@code '/'} character is found, then null is
968     * returned.
969     *
970     * @param urlOrDir This is any java-{@code String}, but preferably one that is a
971     * {@code URL}, or directory.
972     * 
973     * @return the portion of the {@code String} <I><B>before and including</B></I> the final
974     * front-slash {@code '/'} character.  If there are no front-slash characters found in this 
975     * {@code String}, then null.
976     */
977    public static String beforeLastFrontSlashPos(String urlOrDir)
978    {
979        int pos = urlOrDir.lastIndexOf('/');
980        if (pos == -1) return null;
981        return urlOrDir.substring(0, pos + 1);
982    }
983
984
985    // ********************************************************************************************
986    // ********************************************************************************************
987    // Find / From Last-File-Separator
988    // ********************************************************************************************
989    // ********************************************************************************************
990
991
992    /**
993     * This function finds the position of the last {@code 'java.io.File.separator'} character in a
994     * java-{@code String}. In UNIX-based systems, this is a forward-slash {@code '/'} character,
995     * but in Windows-MSDOS, this is a back-slash {@code '\'} character.  Identifying which of the
996     * two is used is obtained by "using" Java's {@code File.separator} class and field.
997     *
998     * @param fileOrDir This may be any Java-{@code String}, but preferably one that represents a
999     * file or directory.
1000     * 
1001     * @return The {@code String}-index of the last 'file-separator' position in a {@code String},
1002     * or {@code -1} if there are no such file-separators.
1003     */
1004    public static int findLastFileSeparatorPos(String fileOrDir)
1005    { return fileOrDir.lastIndexOf(File.separator.charAt(0)); }
1006
1007    /**
1008     * This returns the contents of a {@code String}, after the last
1009     * {@code 'java.io.File.separator'} found. 
1010     * 
1011     * <BR /><BR /><B>NOTE:</B> If no {@code 'java.io.File.separator'} character is found, then
1012     * the original {@code String} is returned.
1013     *
1014     * @param fileOrDir This is any java-{@code String}, but preferably one that is a filename or
1015     * directory-name
1016     * 
1017     * @return the portion of the {@code String} after the final  {@code 'java.io.File.separator'}
1018     * character.  If there are no such characters found, then the original {@code String} shall
1019     * be returned.
1020     */
1021    public static String fromLastFileSeparatorPos(String fileOrDir)
1022    {
1023        int pos = fileOrDir.lastIndexOf(File.separator.charAt(0));
1024        if (pos == -1) return fileOrDir;
1025        return fileOrDir.substring(pos + 1);
1026    }
1027
1028    /**
1029     * This returns the contents of a {@code String}, before the last
1030     * {@code 'java.io.File.separator'} (including the separator itself).
1031     * 
1032     * <BR /><BR /><B>NOTE:</B> If no {@code 'java.io.File.separator'} character is found,
1033     * then null is returned.
1034     *
1035     * @param urlOrDir This is any java-{@code String}, but preferably one that is a
1036     * {@code URL}, or directory.
1037     * 
1038     * @return the portion of the {@code String} <I><B>before and including</B></I> the final
1039     * {@code 'java.io.File.separator'} character.  If there are no such characters found in this 
1040     * {@code String}, then null is returned.
1041     */
1042    public static String beforeLastFileSeparatorPos(String urlOrDir)
1043    {
1044        int pos = urlOrDir.lastIndexOf(File.separator.charAt(0));
1045        if (pos == -1) return null;
1046        return urlOrDir.substring(0, pos + 1);
1047    }
1048
1049
1050    // ********************************************************************************************
1051    // ********************************************************************************************
1052    // Find / From File-Extension
1053    // ********************************************************************************************
1054    // ********************************************************************************************
1055
1056
1057    /**
1058     * This method swaps the ending 'File Extension' with another, parameter-provided, 
1059     * extension.
1060     * 
1061     * @param fileNameOrURLWithExtension Any file-name (or {@code URL}) that has an extension.
1062     * 
1063     * @param newExtension The file or {@code URL} extension used as a substitute for the old
1064     * extension.  This {@code String} may begin with the dot / period character ({@code '.'}),
1065     * and if it does not, one wil be appended.
1066     * 
1067     * @return The new file-name or {@code URL} having the substituted extension.
1068     * 
1069     * @throws StringFormatException If the {@code String} passed does not have any
1070     * {@code '.'} (period) characters, then this exception will throw.
1071     * 
1072     * <BR /><BR /><B STYLE='color:red'>CAUTION:</B> In lieu of an exhaustive check on whether
1073     * or not the input file-name is a valid name, this method will simply check for the presence
1074     * or absence of a period-character ({@code '.'}).  <I>Checking the validity of the input name
1075     * is <B>far</B> beyond the scope of this method.</I>
1076     * 
1077     * <BR /><BR /><B>ALSO:</B> This method shall check to ensure that the {@code 'newExtension'}
1078     * parameter does not have length zero.
1079     * 
1080     * <BR /><BR />To remove a file-extension, use {@link #removeExtension(String)}
1081     */
1082    public static String swapExtension(String fileNameOrURLWithExtension, String newExtension)
1083    {
1084        int dotPos = fileNameOrURLWithExtension.lastIndexOf('.');
1085
1086        if (dotPos == -1) throw new StringFormatException(
1087            "The file-name provided\n[" + fileNameOrURLWithExtension + "]\n" +
1088            "does not have a file-extension"
1089        );
1090
1091        if (newExtension.length() == 0) throw new StringFormatException(
1092            "The new file-name extension has length 0.  " +
1093            " To remove an extension, use 'StringParse.removeFileExtension(fileName)'"
1094        );
1095
1096        return (newExtension.charAt(0) == '.')
1097            ? fileNameOrURLWithExtension.substring(0, dotPos) + newExtension
1098            : fileNameOrURLWithExtension.substring(0, dotPos) + '.' + newExtension;
1099    }
1100
1101    /**
1102     * This method simply removes all character data after the last identified period character
1103     * ({@code '.'}) found within {@code fileNameOrURL}.
1104     * 
1105     * <BR /><BR />If the input-{@code String} does not have a period-character, the original
1106     * {@code String} will be returned, unmodified.
1107     * 
1108     * @param fileNameOrURL Any file-name or {@code URL}, as a {@code String}.
1109     * 
1110     * @return The modified file-name, or {@code URL}, as a {@code String}.
1111     * 
1112     * <BR /><BR /><B STYLE='color:red'>NOTE:</B> No validity checks <I>of any kind</I> are
1113     * performed on {@code 'fileNameOrURL'}.  This method merely checks for the presence or
1114     * absence of a {@code '.'} (period-character), and if it finds one, removes everything
1115     * after-and-including the last-period.
1116     */
1117    public static String removeExtension(String fileNameOrURL)
1118    {
1119        int dotPos = fileNameOrURL.lastIndexOf('.');
1120        if (dotPos == -1) return fileNameOrURL;
1121        return fileNameOrURL.substring(0, dotPos);
1122    }
1123
1124    /**
1125     * This will return the location within a {@code String} where the last period ({@code '.'})
1126     * is found.
1127     * 
1128     * <BR /><BR /><B>ALSO:</B> No validity checks for valid file-system names are performed. 
1129     * Rather, the portion of the input-{@code String} starting at the location of the last period
1130     * is returned, regardless of what the {@code String} contains.
1131     * 
1132     * @param file This may be any Java-{@code String}, but preferably one that represents a
1133     * file.
1134     * 
1135     * @param includeDot When this parameter is passed {@code TRUE}, the position-index that is
1136     * returned will be the location of the last index where a period ({@code '.'}) is found.
1137     * When {@code FALSE}, the index returned will be the location of that period {@code + 1}.
1138     * 
1139     * @return This will return the location of the file-extension.  If no period is found, then
1140     * {@code -1} is returned.  If the period is the last {@code char} in the {@code String},
1141     * and parameter {@code 'includeDot'} is {@code FALSE}, then {@code -1} is returned.
1142     */
1143    public static int findExtension(String file, boolean includeDot)
1144    {
1145        int pos = file.lastIndexOf('.');
1146
1147        if (pos == -1)  return -1;
1148        if (includeDot) return pos;
1149
1150        pos++;
1151        return (pos < file.length()) ? pos : -1;
1152    }
1153
1154    /**
1155     * This returns the contents of a {@code String}, after the last period {@code '.'} in that
1156     * {@code String}.  For file-system and web files, this is often referred to as the <B>file
1157     * extension.</B>
1158     * 
1159     * <BR /><BR /><B>NOTE:</B> If no period {@code '.'} character is found, then null is returned.
1160     *
1161     * <BR /><BR /><B>ALSO:</B> No validity checks for valid file-system names are performed. 
1162     * Rather, the portion of the input-{@code String} starting at the location of the last period
1163     * is returned, regardless of what the {@code String} contains.
1164     * 
1165     * @param file This is any java-{@code String}, but preferably one that is a filename.
1166     * 
1167     * @param includeDot This determines whether the period {@code '.'} is to be included in the
1168     * returned-{@code String}.
1169     * 
1170     * @return the portion of the {@code String} after the final period {@code '.'} character.
1171     * If parameter {@code includeDot} has been passed {@code FALSE}, then the portion of the
1172     * input-{@code String} beginning after the last period is returned.
1173     * 
1174     * <BR /><BR />If there are no period characters found in this {@code String}, then null
1175     * is returned.
1176     */
1177    public static String fromExtension(String file, boolean includeDot)
1178    {
1179        int pos = findExtension(file, includeDot);
1180        if (pos == -1) return null;
1181        return file.substring(pos);
1182    }
1183
1184    /**
1185     * This returns the contents of a {@code String}, before the last period {@code '.'} in that
1186     * {@code String}.  For file-system and web files, this is often referred to as the <B>file
1187     * extension.</B>
1188     * 
1189     * <BR /><BR /><B>NOTE:</B> If no period {@code '.'} character is found, then the original
1190     * {@code String} is returned.
1191     *
1192     * <BR /><BR /><B>ALSO:</B> No validity checks for valid file-system names are performed. 
1193     * Rather, the portion of the input-{@code String} starting at the location of the last period
1194     * is returned, regardless of what the {@code String} contains.
1195     * 
1196     * @param file This is any java-{@code String}, but preferably one that is a filename.
1197     * 
1198     * @return the portion of the {@code String} before the final period {@code '.'} character.
1199     * 
1200     * <BR /><BR />If there are no period characters found in this {@code String}, then the 
1201     * original file is returned.
1202     */
1203    public static String beforeExtension(String file)
1204    {
1205        int pos = file.lastIndexOf('.');
1206        if (pos == -1) return file;
1207        return file.substring(0, pos);
1208    }
1209
1210    /**
1211     * This function returns the root URL-directory of a {@code String}
1212     * 
1213     * <BR /><BR /><B>SPECIFICALLY:</B>  it searches for the "last forward slash" in a
1214     * {@code String}, and returns a substring from position 0 to that point.  If there aren't any
1215     * forward slashes in this {@code String}, null is returned.  The front-slash itself is
1216     * included in the returned {@code String}.
1217     * 
1218     * <BR /><BR /><B>NOTE:</B> It is similar to the old MS-DOS call to "DIR PART"
1219     * 
1220     * @param url Any {@code String} that is intended to be an "Internet URL" - usually
1221     * http://domain/directory/[file]
1222     * 
1223     * @return substring(0, index of last front-slash ({@code '/'}) in {@code String})
1224     */
1225    public static String findURLRoot(String url)
1226    {
1227        int pos = findLastFrontSlashPos(url);
1228
1229        if (pos == -1)  return null;
1230        else            return url.substring(0, pos + 1);
1231    }
1232
1233    /**
1234     * 
1235     * @return After breaking the {@code String} by white-space, this returns the first 'chunk'
1236     * before the first whitespace.
1237     */
1238    public static String firstWord(String s)
1239    {
1240        int pos = s.indexOf(" ");
1241
1242        if (pos == -1)  return s;
1243        else            return s.substring(0, pos);
1244    }
1245
1246
1247    // ********************************************************************************************
1248    // ********************************************************************************************
1249    // Removing parts of a string
1250    // ********************************************************************************************
1251    // ********************************************************************************************
1252
1253
1254    /**
1255     * This function will remove any pairs of Brackets within a {@code String}, and returned the
1256     * paired down {@code String}
1257     * 
1258     * @param s Any {@code String}, which may or may not contain a "Bracket Pair"
1259     * 
1260     * <BR /><BR /><B>For Example:</B>
1261     * 
1262     * <BR /><BR />
1263     * 
1264     * <UL CLASS=JDUL>
1265     * <LI>This {@code String} does contain [a pair of brackets] within!</LI>
1266     * <LI>But this {@code String} does not.</LI>
1267     * </UL>
1268     * 
1269     * @return The same {@code String}, but with any bracket-pairs removed.
1270     */
1271    public static String removeBrackets(String s) { return remove_(s, '[', ']'); }
1272
1273    /**
1274     * Functions the same as {@code removeBrackets(String)} - but removes pairs of curly-braces,
1275     * instead<BR /> <B>NOTE:</B>These are { curly braces } that will be removed by this
1276     * {@code String}!
1277     * 
1278     * @param s Any valid {@code String} { such as } - <I>(even this {@code String})</I>.
1279     * 
1280     * <BR /><BR /><B>For Example:</B>
1281     * 
1282     * <BR /><BR />
1283     * 
1284     * <UL CLASS=JDUL>
1285     * <LI>This {@code String} does contain {a pair of curly-braces} within!</LI>
1286     * <LI>But this {@code String} does not.</LI>
1287     * </UL>
1288     * 
1289     * @return The same {@code String}, but with any curly-brace-pairs removed.
1290     * 
1291     * @see #removeBrackets(String)
1292     */
1293    public static String removeBraces(String s) { return remove_(s, '{', '}'); }
1294
1295    /**
1296     * Removes Parenthesis, similar to other parenthetical removing functions.
1297     * 
1298     * @param s Any (valid) {@code String}.  Below are sample inputs:
1299     * 
1300     * <BR /><BR /><UL CLASS=JDUL>
1301     * <LI>This {@code String} does contain (a pair of parenthesis) within!</LI>
1302     * <LI>But this {@code String} does not.</LI>
1303     * </UL>
1304     * 
1305     * @return The same {@code String}, but with any parenthesis removed.
1306     * 
1307     * @see #removeBrackets(String)
1308     */
1309    public static String removeParens(String s) { return remove_(s, '(', ')'); }
1310
1311    /**
1312     * Removes all parenthetical notations.  Calls all <I><B>remove functions</B></I>
1313     * 
1314     * @param s Any valid string
1315     * 
1316     * @return The same string, but with all parenthesis, curly-brace &amp; bracket pairs removed.
1317     * 
1318     * @see #removeParens(String)
1319     * @see #removeBraces(String)
1320     * @see #removeBrackets(String)
1321     */
1322    public static String removeAllParenthetical(String s)
1323    { return removeParens(removeBraces(removeBrackets(s))); }
1324    
1325    private static String remove_(String s, char left, char right)
1326    {
1327        int p = s.indexOf(left);
1328        if (p == -1) return s;
1329
1330        String ret = s.substring(0, p).trim();
1331
1332        for (++p; (s.charAt(p) != right) && (p < s.length()); p++);
1333
1334        if (p >= (s.length() - 1)) return ret;
1335
1336        ret += " " + s.substring(p + 1).trim();
1337
1338        if (ret.indexOf(left) != -1)    return remove_(ret.trim(), left, right);
1339        else                            return ret.trim();
1340    }
1341
1342
1343
1344    // ********************************************************************************************
1345    // ********************************************************************************************
1346    // Base-64 Encoded Java Objects
1347    // ********************************************************************************************
1348    // ********************************************************************************************
1349
1350
1351    /**
1352     * This will convert any Serializable Java Object into a base-64 String.  This {@code String}
1353     * may be saved, transmitted, <I>even e-mailed to another party, if you wish</I> and decoded
1354     * else-where.
1355     *
1356     * <BR /><BR /><B>REQUIREMENTS:</B>
1357     * 
1358     * <BR /><BR /><OL CLASS=JDOL>
1359     * <LI> Object must implement the {@code interface java.io.Serializable}</LI>
1360     * 
1361     * <LI> Receiving party or storage-device must have access to the {@code .jar file, or .class
1362     *      file(s)} needed to  instantiate that object!  <I>(You must have shared your classes if 
1363     *      you intend to let other people de-serialize instances  of that class)</I>
1364     *      </LI>
1365     * </OL>
1366     * 
1367     * @param o Any java {@code java.lang.Object}.  This object must be Serializable, or else the
1368     * code will generate an exception.
1369     * 
1370     * @return A {@code String} version of this object.  It will be:
1371     * 
1372     * <BR /><BR /><OL CLASS=JDOL>
1373     * <LI> Serialized using the {@code java.io.ObjectOutputStream(...)} <I>object-serialization
1374     *      method</I>
1375     *      </LI>
1376     * 
1377     * <LI> Compressed using the {@code java.io.GZIPOutputStream(...)} <I>stream-compression
1378     *      method</I>
1379     *      </LI>
1380     * <LI>Encoded to a {@code String}, via Base-64 Encoding
1381     * {@code java.util.Base64.getEncoder()}</LI>
1382     * </OL>
1383     *
1384     * <BR /><B><SPAN STYLE="color: red">NOTE:</B></SPAN> Compression does not always make much
1385     * difference, however often times when doing web-scraping projects, there are large Java
1386     * {@code java.util.Vector<String>} filled with many lines of text, and these lists may be
1387     * instantly and easily saved using object-serialization.  Furthermore, in these cases, the
1388     * compression will sometimes reduce file-size by an order of magnitude.
1389     * 
1390     * @see #b64StrToObj(String)
1391     */
1392    public static String objToB64Str(Object o) throws IOException
1393    {
1394        ByteArrayOutputStream   bos     = new ByteArrayOutputStream();
1395        GZIPOutputStream        gzip    = new GZIPOutputStream(bos);
1396        ObjectOutputStream      oos     = new ObjectOutputStream(gzip);
1397
1398        oos.writeObject(o); oos.flush(); gzip.finish(); oos.close(); bos.close();
1399
1400        return Base64.getEncoder().encodeToString(bos.toByteArray());
1401    }
1402
1403    /**
1404     * This converts <B><I>to</B></I> any <I><B>java.io.Serializable</B></I> object
1405     * <I><B>from</B></I> a compressed, serialized, Base-64 Encoded {@code java.lang.String}.  This
1406     * method can be thought of as one which converts objects which have been previously encoded as
1407     * a {@code String}, and possibly even transmitted across the internet, back into an Java
1408     * {@code Object}.
1409     *
1410     * <BR /><BR /><B>REQUIREMENTS:</B> The {@code Object} that is to be instantiated must have its
1411     * class files accessible to the class-loader.  This is the exact-same requirement expected by
1412     * all Java "de-serializations" routines.
1413     * 
1414     * @param str Any previously Base-64 encoded, serialized, compressed {@code java.lang.Object'}
1415     * that has been saved as a {@code String}. That {@code String} should have been generated
1416     * using the {@code Programming.objToB64Str(Object o)} method in this class.
1417     * 
1418     * <BR /><BR /><OL CLASS=JDOL>
1419     * <LI>Serialized using the {@code java.io.ObjectOutputStream(...)} <I>object-serialization
1420     * method</I></LI>
1421     * <LI>Compressed using the {@code java.io.GZIPOutputStream(...)} <I>sream-compression
1422     * method</I></LI>
1423     * <LI>Encoded to a {@code String}, via Base-64 Encoding
1424     * {@code java.util.Base64.getEncoder()}</LI>
1425     * </OL>
1426     *
1427     * <BR /><B><SPAN STYLE="color: red">NOTE:</B></SPAN> Compression does not always make much
1428     * difference, however often times when doing web-scraping projects, there are large Java
1429     * {@code java.util.Vector<String>} filled with many lines of text, and these lists may be
1430     * instantly and easily saved using object-serialization.  Furthermore, in these cases, the
1431     * compression will sometimes reduce file-size by an order of magnitude.
1432     * 
1433     * @return The de-compressed {@code java.lang.Object} converted back from a {@code String}.
1434     * 
1435     * @see #objToB64Str(Object)
1436     */
1437    public static Object b64StrToObj(String str) throws IOException
1438    {
1439        ByteArrayInputStream    bis     = new ByteArrayInputStream(Base64.getDecoder().decode(str));
1440        GZIPInputStream         gzip    = new GZIPInputStream(bis);
1441        ObjectInputStream       ois     = new ObjectInputStream(gzip);
1442        Object                  ret     = null;
1443
1444        try
1445            { ret = ois.readObject(); }
1446        catch (ClassNotFoundException e)
1447        {
1448            throw new IOException(
1449                "There were no serialized objects found in your String.  See e.getCause();",
1450                e
1451            );
1452        }
1453
1454        bis.close(); ois.close();
1455        return ret;
1456    }
1457
1458    /**
1459     * This performs an identical operation as the method: {@code objToB64Str}, however it
1460     * generates an output {@code String} that is "MIME" compatible.  All this means is that the
1461     * {@code String} itself - <I>which could conceivable by thousands or even hundreds of
1462     * thousands of characters long</I> - will have {@code new-line characters} inserted such that
1463     * it may be printed on paper or included in a text-file that is (slightly) more
1464     * human-readable.  Base64 MIME encoded {@code String's} look like very long paragraphs of
1465     * random-text data, while regular Base64-encodings are a single, very-long, {@code String}
1466     * with no space characters.
1467     * 
1468     * @param o Any {@code java.lang.Object}.  This object must be Serializable, or else the code
1469     * will generate an exception.
1470     * 
1471     * @return A Base-64 MIME Encoded {@code String} version of any serializable
1472     * {@code java.lang.Object}.
1473     * 
1474     * @see #objToB64Str(Object)
1475     * @see #b64MimeStrToObj(String)
1476     */
1477    public static String objToB64MimeStr(Object o) throws IOException
1478    {
1479        ByteArrayOutputStream   bos     = new ByteArrayOutputStream();
1480        GZIPOutputStream        gzip    = new GZIPOutputStream(bos);
1481        ObjectOutputStream      oos     = new ObjectOutputStream(gzip);
1482
1483        oos.writeObject(o); oos.flush(); gzip.finish(); oos.close(); bos.close();
1484
1485        return Base64.getMimeEncoder().encodeToString(bos.toByteArray());
1486    }
1487
1488    /**
1489     * This performs an identical operation as the method: {@code b64StrToObj}, however receives a
1490     * "MIME" compatible encoded {@code String}.  All this means is that the {@code String} itself
1491     * - <I>which could conceivable by thousands or even hundreds of thousands of characters
1492     * long</I> - will have {@code new-line characters} inserted such that it may be printed on
1493     * paper or included in a text-file that is (slightly) more human-readable.  Base64 MIME
1494     * encoded {@code String's} look like very long paragraphs of random-text data, while regular
1495     * Base64 encodings a single, very-long, {@code String's}.
1496     * 
1497     * @return The (de-serialized) java object that was read from the input parameter
1498     * {@code String 'str'}
1499     * 
1500     * <BR /><BR /><B>REQUIREMENTS:</B> The object that is to be instantiated must have its class
1501     * files accessible to the class-loader.  This is the exact-same requirement expected by all
1502     * Java "de-serializations" routines.
1503     * 
1504     * @see #b64StrToObj(String)
1505     * @see #objToB64MimeStr(Object)
1506     */
1507    public static Object b64MimeStrToObj(String str) throws IOException
1508    {
1509        ByteArrayInputStream    bis     = new ByteArrayInputStream(Base64.getMimeDecoder().decode(str));
1510        GZIPInputStream         gzip    = new GZIPInputStream(bis);
1511        ObjectInputStream       ois     = new ObjectInputStream(gzip);
1512        Object                  ret     = null;
1513
1514        try
1515            { ret = ois.readObject(); }
1516        catch (ClassNotFoundException e)
1517        { 
1518            throw new IOException(
1519                "There were no serialized objects found in your String.  See e.getCause();",
1520                e
1521            );
1522        }
1523
1524        bis.close(); ois.close();
1525        return ret;
1526    }
1527
1528
1529    // ********************************************************************************************
1530    // ********************************************************************************************
1531    // '../' (Parent Directory)
1532    // ********************************************************************************************
1533    // ********************************************************************************************
1534
1535
1536    /**
1537     * Computes a "relative {@code URL String}".
1538     * 
1539     * @param fileName This is a fileName whose ancestor directory needs to be
1540     * <I>'relative-ised'</I>
1541     * 
1542     * @param ancestorDirectory This is an ancestor (container) directory.
1543     * 
1544     * @param separator The separator character used to separate file-system directory names.
1545     * 
1546     * @return This shall return the "../.." structure needed to insert a relative-{@code URL} or
1547     * link into a web-page.
1548     * 
1549     * @throws IllegalArgumentException This exception shall throw if the separator character is
1550     * not one of the standard file &amp; directory separators: forward-slash {@code '/'} or
1551     * back-slash {@code '\'}.
1552     * 
1553     * <BR /><BR />This exception also throws if the {@code String} provided to parameter
1554     * {@code 'fileName'} does not begin-with the {@code String} provided to parameter
1555     * {@code 'ancestorDirectory'}.
1556     */
1557    public static String dotDots(String fileName, String ancestorDirectory, char separator)
1558    {
1559        if ((separator != '/') && (separator != '\\')) throw new IllegalArgumentException(
1560            "The separator character provided to this method must be either a forward-slash '/' " +
1561            "or a back-slash ('\\') character.  You have provided: ['" + separator + "']."
1562        );
1563
1564        if (! fileName.startsWith(ancestorDirectory)) throw new IllegalArgumentException(
1565            "The file-name you have provided [" + fileName + "] is a String that does " +
1566            "start with the ancestorDirectory String [" + ancestorDirectory + "].  " +
1567            "Therefore there is no relative path using the dot-dot construct to the named " +
1568            "ancestor directory fromm the directory where the named file resides."
1569        );
1570
1571        int levelsDeep = StringParse.countCharacters(fileName, separator) - 
1572            StringParse.countCharacters(ancestorDirectory, separator);
1573
1574        String dotDots = "";
1575
1576        while (levelsDeep-- > 0) dotDots = dotDots + ".." + separator;
1577
1578        return dotDots;
1579    }
1580
1581    /**
1582     * Convenience Method.
1583     * <BR />Invokes: {@link #dotDotParentDirectory(String, char, short)}
1584     * <BR />Converts: {@code URL} to {@code String}, eliminates non-essential
1585     * {@code URI}-information (Such as: {@code ASP, JSP, PHP Query-Strings, and others too})
1586     * <BR />Passes: {@code char '/'}, the separator character used in {@code URL's}
1587     * <BR />Passes: {@code '1'} to parameter {@code 'nLevels'} - only going up on directory
1588     */
1589    public static String dotDotParentDirectory(URL url)
1590    {
1591        String urlStr = url.getProtocol() + "://" + url.getHost() + url.getPath();
1592        return dotDotParentDirectory(urlStr, '/', (short) 1);
1593    }
1594
1595    /**
1596     * Convenience Method.
1597     * <BR />Invokes: {@link #dotDotParentDirectory(String, char, short)}
1598     * <BR />Passes: {@code char '/'}, the separator character used in {@code URL's}
1599     * <BR />Passes: {@code '1'} to parameter {@code 'nLevels'} - only going up on directory
1600     */
1601    public static String dotDotParentDirectory(String urlAsStr)
1602    { return dotDotParentDirectory(urlAsStr, '/', (short) 1); }
1603
1604    /**
1605     * Convenience Method.
1606     * <BR />Invokes: {@link #dotDotParentDirectory(String, char, short)}
1607     * <BR />Converts: {@code URL} to {@code String}, eliminates non-essential
1608     * {@code URI}-information (Such as: {@code ASP, JSP, PHP Query-Strings, and others too})
1609     * <BR />Passes: {@code char '/'}, the separator character used in {@code URL's}
1610     */
1611    public static String dotDotParentDirectory(URL url, short nLevels)
1612    {
1613        String urlStr = url.getProtocol() + "://" + url.getHost() + url.getPath();
1614        return dotDotParentDirectory(urlStr, '/', nLevels);
1615    }
1616
1617    /** 
1618     * Convenience Method.
1619     * <BR />Invokes: {@link #dotDotParentDirectory(String, char, short)}
1620     * <BR />Passes: {@code char '/'}, the separator character used in {@code URL's}
1621     */
1622    public static String dotDotParentDirectory(String urlAsStr, short nLevels)
1623    { return dotDotParentDirectory(urlAsStr, '/', nLevels); }
1624
1625    /**
1626     * Convenience Method.
1627     * <BR />Invokes: {@link #dotDotParentDirectory(String, char, short)}.
1628     * <BR />Passes: {@code '1'} to parameter {@code nLevels} - only going up one directory.
1629     */ 
1630    public static String dotDotParentDirectory(String directoryStr, char dirSeparator)
1631    { return dotDotParentDirectory(directoryStr, dirSeparator, (short) 1); }
1632
1633    /**
1634     * This does traverses up a directory-tree structure, and returns a 'parent-level' directory
1635     * that is {@code 'nLevels'} up the tree.
1636     *
1637     * <BR /><BR /><B>NOTE:</B> The character used as the "File Separator" and/or "Directory
1638     * Separator" can be obtained using the field: {@code java.io.File.Separator.charAt(0).}  The
1639     * class {@code java.io.File} provides access to the file-separator used by the file-system on
1640     * which the JVM is currently running, although it treats it as a multi-character
1641     * {@code String}. Just use the commonly-used java method {@code 'charAt(0)'} to obtain the
1642     * forward-slash {@code '/'} or backward-slash {@code '\'} character. 
1643     * 
1644     * <BR /><BR /><B><SPAN STYLE="color: red;">IMPORTANT:</B></SPAN> There is no error-checking
1645     * performed by this method regarding whether the input {@code String} represents a valid file
1646     * or directory.   Instead, this method just looks for the <I><B>second from last
1647     * separator-character (usually a {@code '/'} forward-slash char)</B></I> and returns a
1648     * substring that starts at index 0, and continues to that position-plus-1 (in order to include
1649     * that second-to-last separator char).
1650     *
1651     * @param directoryStr This may be any java-{@code String}, although it is expected to be on
1652     * which represents the file &amp; directory structure of file on the file-system.  It may also
1653     * be {@code URL} for a web-site
1654     * 
1655     * @param separator This is the separator currently used by that file &amp; directory system.
1656     * If trying to find the parent directory of a {@code URL}, this should be the forward-slash
1657     * character {@code '/'}.
1658     * 
1659     * @param nLevels This is how many "parent-level directories" (how many levels up the tree)
1660     * need to be computed. This parameter must '1' or greater.  If the passed parameter
1661     * {@code 'directoryStr'} does not contain enough directories to traverse up the tree, then
1662     * this method will throw an {@code IllegalArgumentException}.
1663     *
1664     * @return a {@code String} that represents 'nLevels' up the directory tree, either for
1665     * a directory on the local-file system, or on a web-server from a Uniform Resource 
1666     * Locator.
1667     *
1668     * @throws IllegalArgumentException If the value of parameter {@code short 'nLevels'} is
1669     * negative, or does not identify a number consistent with the number of directories that are
1670     * contained by the input urlAsStr parameter.
1671     * 
1672     * <BR /><BR />This exception shall also throw if the {@code 'separator'} character is not one
1673     * of the standard file &amp; directory separators: forward-slash {@code '/'} or back-slash
1674     * {@code '\'}.
1675     */
1676    public static String dotDotParentDirectory(String directoryStr, char separator, short nLevels)
1677    {
1678        if (nLevels < 1) throw new IllegalArgumentException(
1679            "The parameter nLevels may not be less than 1, nor negative.  You have passed: " + nLevels
1680        );
1681
1682        if ((separator != '/') && (separator != '\\')) throw new IllegalArgumentException(
1683            "The separator character provided to this method must be either a forward-slash '/' " +
1684            "or a back-slash ('\\') character.  You have provided: ['" + separator + "']."
1685        );
1686
1687        int count = 0;
1688
1689        for (int i=directoryStr.length() - 1; i >= 0; i--)
1690            if (directoryStr.charAt(i) == separator)
1691                if (++count == (nLevels + 1))
1692                    return directoryStr.substring(0, i + 1);
1693
1694        throw new IllegalArgumentException(
1695            "The parameter nLevels was: " + nLevels + ", but unfortunately there only were: " + count +
1696            "'" + separator + "' characters found in the directory-string."
1697        );
1698    }
1699
1700
1701    // ********************************************************************************************
1702    // ********************************************************************************************
1703    // Quick 'isNumber' methods
1704    // ********************************************************************************************
1705    // ********************************************************************************************
1706
1707
1708    /**
1709     * Determines, efficiently, whether an input {@code String} is also an integer.
1710     * 
1711     * <BR /><BR /><B>NOTE:</B> A leading plus-sign ({@code '+'}) will, in fact, generate a
1712     * {@code FALSE} return-value for this method.
1713     * 
1714     * @param s Any java {@code String}
1715     * 
1716     * @return {@code TRUE} if the input {@code String} is any integer, and false otherwise.
1717     * 
1718     * <BR /><BR /><B>NOTE:</B> This method does not check whether the number, itself, will
1719     * actually fit into a field or variable of type {@code 'int'}.  For example, the input 
1720     * {@code String '12345678901234567890'} (a very large integer), though an integer from a
1721     * mathematical perspective, is not a valid java {@code 'int'}.  In such cases, {@code TRUE}
1722     * is returned, but if Java's {@code Integer.parseInt} method were subsequently used, that
1723     * method would throw an exception.
1724     * 
1725     * <BR /><BR /><B>NOTE:</B> The primary purpose of this method is to avoid having to write
1726     * {@code try {} catch (NumberFormatException)} code-blocks.  Furthermore, if only a check
1727     * is desired, and the {@code String} does not actually need to be converted to a number,
1728     * this is also more efficient than actually performing the conversion.
1729     * 
1730     * @see #isInt(String)
1731     */
1732    public static boolean isInteger(String s)
1733    {
1734        if (s == null) return false;
1735
1736        int length = s.length();
1737
1738        if (length == 0) return false;
1739
1740        int i = 0;
1741
1742        if (s.charAt(0) == '-')
1743        {
1744            if (length == 1) return false;
1745            i = 1;
1746        }
1747
1748        while (i < length)
1749        {
1750            char c = s.charAt(i++);
1751            if (c < '0' || c > '9') return false;
1752        }
1753
1754        return true;
1755    }
1756
1757    /**
1758     * Convenience Method.
1759     * <BR />Invokes: {@link #isOfPrimitiveType(String, char[])}
1760     * <BR />Passes: The ASCII characters that comprise {@code Integer.MIN_VALUE}
1761     */
1762    public static boolean isInt(String s)
1763    { return isOfPrimitiveType(s, INT_MIN_VALUE_DIGITS_AS_CHARS); }
1764
1765    /**
1766     * Convenience Method.
1767     * <BR />Invokes: {@link #isOfPrimitiveType(String, char[])}
1768     * <BR />Passes: The ASCII characters that comprise {@code Long.MIN_VALUE}
1769     */
1770    public static boolean isLong(String s)
1771    { return isOfPrimitiveType(s, LONG_MIN_VALUE_DIGITS_AS_CHARS); }
1772
1773    /**
1774     * Convenience Method.
1775     * <BR />Invokes: {@link #isOfPrimitiveType(String, char[])}
1776     * <BR />Passes: ASCII characters that comprise {@code Byte.MIN_VALUE}
1777     */
1778    public static boolean isByte(String s)
1779    { return isOfPrimitiveType(s, BYTE_MIN_VALUE_DIGITS_AS_CHARS); }
1780
1781    /**
1782     * Convenience Method.
1783     * <BR />Invokes: {@link #isOfPrimitiveType(String, char[])}
1784     * <BR />Passes: ASCII characters that comprise {@code Short.MIN_VALUE}
1785     */
1786    public static boolean isShort(String s)
1787    { return isOfPrimitiveType(s, SHORT_MIN_VALUE_DIGITS_AS_CHARS); }
1788
1789
1790    /**
1791     * Determines whether the input {@code String} is an integer in the range of Java's primitive
1792     * type specified by an input {@code char[]} array parameter.  Specifically, if the the input
1793     * {@code String} is both a mathematical integer, and also an integer in the range of
1794     * {@code MIN_VALUE} and {@code MAX_VALUE} for that primitive-type and then (and only then)
1795     * will {@code TRUE} be returned.
1796     * 
1797     * <BR /><BR /><B>NOTE:</B> The max and min values in which the range of valid integers 
1798     * <B><I>must reside</I></B> (for primitive-type {@code 'int'}, for instance) are as below:
1799     * {@code -2147483648} ... {@code 2147483647}.
1800     * 
1801     * <BR /><BR /><B>ALSO:</B> A leading plus-sign ({@code '+'}) will, in fact, generate a
1802     * {@code FALSE} return-value for this method.
1803     * 
1804     * @param s Any Java {@code String}
1805     * 
1806     * @param minArr The value of a Java Primitive {@code MIN_VALUE}, without the minus-sign,
1807     * represented as a {@code char[]} array.
1808     * 
1809     * <TABLE CLASS=JDBriefTable>
1810     * <TR> <TH>Primitive Type</TH>  <TH>Integer as ASCII {@code char[]} array</TH></TR>
1811     * <TR> <TD>{@code byte}</TD>    <TD>{@code '2', '5', '6'}</TD></TR>
1812     * <TR> <TD>{@code short}</TD>   <TD>{@code '6', '5', '5', '3', '6'}</TD></TR>
1813     * 
1814     * <TR> <TD>{@code int}</TD>
1815     *      <TD>{@code '2', '1', '4', '7,' '4', '8', '3', '6', '4', '8'}</TD>
1816     *      </TR>
1817     * 
1818     * <TR> <TD>{@code long}</TD>
1819     *      <TD>{@code '2', '1', '4', '9', '2', '2', '3', '3', '7', '2', '0', '3', '6', '8', '5',
1820     *           '4', '7', '7', '5', '8', '0', '8'}</TD>
1821     *      </TR>
1822     * </TABLE>
1823     * 
1824     * @return {@code TRUE} If the input {@code String} is both an integer, and also one which
1825     * falls in the range comprised by the specified Java Primitive Type.  Return {@code FALSE}
1826     * otherwise.
1827     * 
1828     * <BR /><BR /><B>NOTE:</B> The primary purpose of this method is to avoid having to write
1829     * {@code try {} catch (NumberFormatException)} code-blocks.  Furthermore, if only a check
1830     * is desired, and the {@code String} does not actually need to be converted to a number,
1831     * this is also more efficient than actually performing the conversion.
1832     * 
1833     * @see #isInteger(String)
1834     * @see #isInt(String)
1835     * @see #isByte(String)
1836     * @see #isLong(String)
1837     * @see #isShort(String)
1838     */
1839    protected static boolean isOfPrimitiveType(String s, char[] minArr)
1840    {
1841        int length = s.length();
1842
1843        // Zero length string's are not valid integers.
1844        if (length == 0)                                    return false;
1845
1846        // A negative integer may begin with a minus-sign.
1847        boolean negative = s.charAt(0) == '-';
1848
1849        // ****************************************************************************************
1850        // If the string is too short or too long, this method doesn't need to do any work.
1851        // We either know the answer immediately (too long), or we can call the simpler method
1852        // (in the case that it is too short)
1853        // ****************************************************************************************
1854
1855        // If a string is shorter than (for type 'int', for example): 2147483647 (10 chars)
1856        // then we ought use the simplified method which just checks if the string is an integer.
1857        if (length < minArr.length) return isInteger(s);
1858
1859        // If the string is longer than (for type 'int', for example): -2147483648 (11 chars)
1860        // then it cannot be an integer that fits into primitive 'int', so return false.
1861        if (length > (minArr.length + 1)) return false;
1862
1863        // If the String is *EXACTLY* 11 characters long (for primitive-type 'int', for example),
1864        // but doesn't begin with a negative sign, we also know the answer immediately.
1865        if ((!negative) && (length == (minArr.length + 1))) return false;
1866
1867        // If the String *EXACTLY* the length of MAX_NUUMBER, but it begins with a negative sign,
1868        // we can call the simplified method, instead as well.
1869        if (negative && (length == minArr.length)) return isInteger(s);
1870
1871        // The **REST** of the code is only executed if the numeric part of the String
1872        // (Specifically: leaving out the '-' negative sign, which may or may not be present)
1873        // ... if the numeric part of the String is precisely the length of MAX_VALUE / MAX_NUMBER
1874        // as determined by the length of the array 'minArr'...  If the input string is
1875        // **PRECISELY** that length, then the string must be checked in the loop below. 
1876
1877        int     i                       = negative ? 1 : 0;
1878        int     j                       = 0;
1879        boolean guaranteedFitIfInteger  = false;
1880        char    c                       = 0;
1881
1882        while (i < length)
1883        {
1884            c = s.charAt(i);
1885
1886            if (! guaranteedFitIfInteger)
1887            {
1888                if (c > minArr[j]) return false;
1889                if (c < minArr[j]) guaranteedFitIfInteger = true;
1890            }
1891
1892            if (c < '0') return false;
1893            if (c > '9') return false;
1894
1895            i++; j++;
1896        }
1897
1898        // THE COMMENT BELOW DELINEATES WHAT HAPPENS FOR THE INPUT-CASE OF PRIMITIVE-TYPE 'INT'
1899        // (2147483648)... But it generalizes for byte, short, and long as well.
1900
1901        // This might seem very strange.  Since the MIN_VALUE ends with an '8', but the
1902        // MAX_VALUE ends with a '7', and since we are checking each character to see that
1903        // it falls within the array above, **RATHER THAN** just returning TRUE right here,
1904        // we have to catch the **LONE** border/edge case where some joker actually passed the
1905        // String 2147483648 - which must return FALSE, since the last positive integer is
1906        // 2147483647 (see that it has an ending of '7', rather than an '8').
1907
1908        return guaranteedFitIfInteger || negative || (c != minArr[minArr.length-1]);
1909    }
1910
1911    private static final String Digits = "(\\p{Digit}+)";
1912    private static final String HexDigits  = "(\\p{XDigit}+)";
1913
1914    // an exponent is 'e' or 'E' followed by an optionally
1915    // signed decimal integer.
1916
1917    private static final String Exp = "[eE][+-]?"+Digits;
1918
1919    /**
1920     * A Predicate which uses a regular-expression for checking whether a {@code String} is a valid
1921     * &amp; parseable {@code double}, which is guaranteed not to throw a
1922     * {@code NumberFormatException} when using the parser {@code Double.parseDouble}.
1923     * 
1924     * <BR /><BR /><SPAN CLASS=CopiedJDK>The Following Description is Directly Copied From:
1925     * {@code java.lang.Double.valueOf(String)}, <B>JDK 1.8</B></SPAN>
1926     * 
1927     * <EMBED CLASS='external-html' DATA-FILE-ID=STRP_D_VALUEOF>
1928     * 
1929     * @see #floatingPointPred
1930     * @see #isDouble(String)
1931     */
1932    public static final Pattern FLOATING_POINT_REGEX = Pattern.compile(
1933        // NOTE: Digits, HexDigits & Exp defined ABOVE
1934
1935        "[\\x00-\\x20]*"+   // Optional leading "whitespace"
1936        "[+-]?(" +          // Optional sign character
1937        "NaN|" +            // "NaN" string
1938        "Infinity|" +       // "Infinity" string
1939  
1940        // A decimal floating-point string representing a finite positive
1941        // number without a leading sign has at most five basic pieces:
1942        // Digits . Digits ExponentPart FloatTypeSuffix
1943        //
1944        // Since this method allows integer-only strings as input
1945        // in addition to strings of floating-point literals, the
1946        // two sub-patterns below are simplifications of the grammar
1947        // productions from section 3.10.2 of
1948        // The Java Language Specification.
1949  
1950        // Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt
1951        "((("+Digits+"(\\.)?("+Digits+"?)("+Exp+")?)|"+
1952  
1953        // . Digits ExponentPart_opt FloatTypeSuffix_opt
1954        "(\\.("+Digits+")("+Exp+")?)|"+
1955  
1956        // Hexadecimal strings
1957        "((" +
1958
1959        // 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt
1960        "(0[xX]" + HexDigits + "(\\.)?)|" +
1961  
1962        // 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt
1963        "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" +
1964  
1965        ")[pP][+-]?" + Digits + "))" +
1966        "[fFdD]?))" +
1967        "[\\x00-\\x20]*"
1968        // Optional trailing "whitespace";
1969    );
1970
1971    /**
1972     * This is the floating-point regular-expression, simply converted to a predicate.
1973     * @see #FLOATING_POINT_REGEX
1974     * @see #isDouble(String)
1975     */
1976    public static final Predicate<String> floatingPointPred = FLOATING_POINT_REGEX.asPredicate();
1977
1978    /**
1979     * Tests whether an input-{@code String} can be parsed into a {@code double}, without throwing
1980     * an exception.
1981     * 
1982     * @return {@code TRUE} <I>if and only if</I> calling {@code Double.valueOf(s)} (or
1983     * {@code Double.parseDouble(s)}) is guaranteed to produce a result, without throwing a
1984     * {@code NumberFormatException}.
1985     * 
1986     * <BR /><BR /><B STYLE='color: red;'>NOTE:</B> Whenever analyzing performance and
1987     * optimizations, it is important to know just "how costly" (as an order of magnitude) a
1988     * certain operation really is.  Constructors, for instance, that don't allocated much memory
1989     * can be two orders of magnitude <I>less costly than</I> the JRE's costs for creating the
1990     * {@code StackTrace} object when an exception (such as {@code NumberFormatException}) is
1991     * thrown.
1992     * 
1993     * <BR /><BR />Though it costs "extra" to check whether a {@code String} can be parsed by the
1994     * Double-String Parser, if the programmer expects that exceptions will occasionally occur, the
1995     * amount of time saved by checking a {@code String} before parsing it as a Double-String will
1996     * actually save time - <I>even if only 1 in 500 of those {@code String's} are invalid and
1997     * would throw the exception, causing a {@code StackTrace} constructor to be invoked.</I>
1998     * 
1999     * @see #FLOATING_POINT_REGEX
2000     * @see #floatingPointPred
2001     */
2002    public static boolean isDouble(String s)
2003    { return floatingPointPred.test(s); }
2004}