001package Torello.Java;
002
003import Torello.JavaDoc.Annotations.LinkJavaSource;
004
005import Torello.Java.ReadOnly.ReadOnlyArrayList;
006import Torello.Java.ReadOnly.ReadOnlyList;
007
008import Torello.Java.Function.IntTFunction;
009import static Torello.Java.C.RESET;
010
011import java.util.Calendar;
012import java.util.Locale;
013import java.util.Arrays;
014
015import java.text.DecimalFormat;
016
017/**
018 * This class provides several {@code String} printing utilities such as abbreviation and list
019 * printing.
020 */
021@Torello.JavaDoc.Annotations.StaticFunctional
022public class StrPrint
023{
024    private StrPrint() { }
025
026    /** A Java-String containing four consecutive space chars ({@code ' '} ASCII #32 / #20h) */
027    public static final String I4 = StringParse.nChars(' ', 4);
028
029    /** A Java-String containing eight consecutive space chars ({@code ' '} ASCII #32 / #20h) */
030    public static final String I8 = StringParse.nChars(' ', 8);
031
032    /** A Java-String containing twelve consecutive space chars ({@code ' '} ASCII #32 / #20h) */
033    public static final String I12 = StringParse.nChars(' ', 12);
034
035    /** A Java-String containing sixteen consecutive space chars ({@code ' '} ASCII #32 / #20h) */
036    public static final String I16 = StringParse.nChars(' ', 16);
037
038    /** A Java-String containing twenty consecutive space chars ({@code ' '} ASCII #32 / #20h) */
039    public static final String I20 = StringParse.nChars(' ', 20);
040
041
042    // ********************************************************************************************
043    // ********************************************************************************************
044    // HELPER & BASIC
045    // ********************************************************************************************
046    // ********************************************************************************************
047
048
049    /**
050     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_NLAT_DESC>
051     * 
052     * @param s Any {@code java.lang.String}, preferably one with multiple lines of text.
053     * 
054     * @return A {@code String}, where each line of text has been "trimmed", and the two
055     * character sequence {@code "\\n"} inserted in-between each line.
056     * 
057     * @see #abbrevStartRDSF(String, int, boolean)
058     * @see #abbrevEndRDSF(String, int, boolean)
059     */
060    public static String newLinesAsText(String s)
061    {
062        return s
063            .replaceAll(
064                    // White-Space-Except-Newline, THEN newline, THEN White-SpaceExcept-Newline
065                    "[ \t\r\f\b]*\n[ \t\r\f\b]*",
066
067                    // Replace Each Occurence of that with:
068                    // == COMPILES-TO ==> "\\n" == REG-EX-READS ==> BackSlash and letter 'n'
069                    "\\\\n"
070            )
071            // == COMPILES-TO ==> "\s+" == REG-EX-READS ==> 'spaces'
072            .replaceAll("\\s+", " ")
073
074            // Don't forget about leading and trailing stuff...
075            .trim();
076    }
077
078
079    // ********************************************************************************************
080    // ********************************************************************************************
081    // Abbreviating Text, with "newLinesAsText" - Helper
082    // ********************************************************************************************
083    // ********************************************************************************************
084
085
086    /**
087     * Convenience Method.
088     * <BR /><B STYLE='color: red;'>RDSF: Remove Duplicate Spaces First</B>
089     * <BR />Invokes:    {@link StringParse#removeDuplicateSpaces(String)} 
090     * <BR />Or Invokes: {@link #newLinesAsText(String)}
091     * <BR />Finally:    {@link #abbrevStart(String, boolean, int)}
092     */
093    public static String abbrevStartRDSF
094        (String s, int maxLength, boolean seeEscapedNewLinesAsText)
095    {
096        // false is passed to 'abbrevStart' parameter 'escapeNewLines' because in both scenarios
097        // of this conditional-statement, the new-lines have already been removed by the previous
098        // method call.
099        //
100        // both 'removeDuplicateSpaces' and 'newLinesAsText' remove the new-lines
101
102        return seeEscapedNewLinesAsText
103            ? abbrevStart(newLinesAsText(s), false, maxLength)
104            : abbrevStart(StringParse.removeDuplicateSpaces(s), false, maxLength);
105    }
106
107    /**
108     * Convenience Method.
109     * <BR /><B STYLE='color: red;'>RDSF: Remove Duplicate Spaces First</B>
110     * <BR />Invokes: {@link StringParse#removeDuplicateSpaces(String)}
111     * <BR />Or Invokes: {@link #newLinesAsText(String)}
112     * <BR />Finally: {@link #abbrevEnd(String, boolean, int)}
113     */
114    public static String abbrevEndRDSF
115        (String s, int maxLength, boolean seeEscapedNewLinesAsText)
116    {
117        // false is passed to 'abbrevStart' parameter 'escapeNewLines' because in both scenarios
118        // of this conditional-statement, the new-lines have already been removed by the previous
119        // method call.
120        //
121        // both 'removeDuplicateSpaces' and 'newLinesAsText' remove the new-lines
122
123        return seeEscapedNewLinesAsText
124            ? abbrevEnd(newLinesAsText(s), false, maxLength)
125            : abbrevEnd(StringParse.removeDuplicateSpaces(s), false, maxLength);
126    }
127
128    /**
129     * Convenience Method.
130     * <BR />Passes: {@code '0'} to parameter {@code 'abbrevPos'}, forcing the abbreviation to
131     * occur at the <B>start</B> of the {@code String} (if long enough to be abbreviated)
132     * <BR />See Documentation: {@link #abbrev(String, int, boolean, String, int)}
133     */
134    @LinkJavaSource(handle="Abbrev", name="print1")
135    public static String abbrevStart(String s, boolean escNewLines, int maxLength)
136    { return Abbrev.print1(s, 0, escNewLines, null, maxLength); }
137
138    /**
139     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_1_DESC>
140     * @param s This may be any Java (non-null) {@code String}
141     *
142     * @param abbrevPos This parameter is used to indicate where the abbreviation-{@code String}
143     * should occur - <I>if this {@code String 's'} is long enough to be abbreviated.</I>  For
144     * instance, if {@code '0'} (zero) were passed to this parameter, and {@code 's'} were longer
145     * than parameter {@code 'maxLength'}, then an ellipsis would be appended to the beginning of
146     * the returned-{@code 'String'}.  (Or, if some other {@code 'abbrevStr'} were specified, that
147     * other abbreviation would be appended to the beginning of the returned-{@code String})
148     * 
149     * @param escapeNewLines            <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_ENL>
150     * @param abbrevStr                 <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_STR>
151     * @param maxLength                 <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_MXL>
152     * @return                          <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_1_RET>
153     * @throws IllegalArgumentException <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_1_IAEX>
154     */
155    @LinkJavaSource(handle="Abbrev", name="print1")
156    public static String abbrev(
157            String  s,
158            int     abbrevPos,
159            boolean escapeNewLines,
160            String  abbrevStr,
161            int     maxLength
162        )
163    { return Abbrev.print1(s, abbrevPos, escapeNewLines, abbrevStr, maxLength); }
164
165    /**
166     * Convenience Method.
167     * <BR />Parameter: {@code spaceBeforeAbbrev} set to {@code FALSE}
168     * <BR />Abbreviates: Default ellipsis ({@code '...'}) are placed at the end of the
169     * {@code String}
170     * <BR />See Documentation: {@link #abbrev(String, boolean, boolean, String, int)}
171     */
172    @LinkJavaSource(handle="Abbrev", name="print2")
173    public static String abbrevEnd(String s, boolean escapeNewLines, int maxLength)
174    { return Abbrev.print2(s, false, escapeNewLines, null, maxLength); }
175
176    /**
177     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_2_DESC>
178     * @param s This may be any Java (non-null) {@code String}
179     *
180     * @param spaceBeforeAbbrev This ensures that for whatever variant of ellipsis being used, the
181     * space-character is inserted directly before appending the ellipsis {@code "..."} or the
182     * user-provided {@code 'abbrevStr'}.
183     *
184     * @param escapeNewLines            <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_ENL>
185     * @param abbrevStr                 <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_STR>
186     * @param maxLength                 <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_MXL>
187     * @return                          <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_2_RET>
188     * @throws IllegalArgumentException <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_2_IAEX>
189     */
190    @LinkJavaSource(handle="Abbrev", name="print2")
191    public static String abbrev(
192            String  s,
193            boolean spaceBeforeAbbrev,
194            boolean escapeNewLines,
195            String  abbrevStr,
196            int     maxLength
197        )
198    { return Abbrev.print2(s, spaceBeforeAbbrev, escapeNewLines, abbrevStr, maxLength); }
199
200
201    // ********************************************************************************************
202    // ********************************************************************************************
203    // Abbreviated List Printing
204    // ********************************************************************************************
205    // ********************************************************************************************
206
207
208    /**
209     * Convenience Method.
210     * <BR />Passes: {@code Object.toString()} to {@code 'listItemPrinter'}
211     * <BR />See Documentation: {@link #printListAbbrev(Iterable, IntTFunction, int, int, boolean,
212     *  boolean, boolean)}
213     */
214    @LinkJavaSource(handle="PrintListAbbrev", name="print1")
215    public static <ELEM> String printListAbbrev(
216            Iterable<ELEM> list, int lineWidth, int indentation, boolean seeEscapedNewLinesAsText,
217            boolean printNulls, boolean showLineNumbers
218        )
219    {
220        return PrintListAbbrev.print1(
221            list, (int i, Object o) -> o.toString(), lineWidth, indentation,
222            seeEscapedNewLinesAsText, printNulls, showLineNumbers
223        );
224    }
225
226    /**
227     * <EMBED CLASS=defs DATA-LIST_TYPE=Array>
228     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_DESCRIPTION>
229     * @param list Any iterable-list of Java Object's
230     * 
231     * @param listItemPrinter
232     * <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_LIST_ITEM_PR>
233     * 
234     * @param lineWidth                 <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_LINE_WIDTH>
235     * @param indentation               <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_INDENTATION>
236     * @param seeEscapedNewLinesAsText  <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_SEE_ESC_NL>
237     * @param printNulls                <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_PRINT_NULLS>
238     * @param showLineNumbers           <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_SHOW_LNUMS>
239     * @return                          <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_RETURNS>
240     * 
241     * @see #zeroPad10e2(int)
242     * @see #abbrevEndRDSF(String, int, boolean)
243     * @see StringParse#nChars(char, int)
244     * @see StringParse#trimLeft(String)
245     */
246    @LinkJavaSource(handle="PrintListAbbrev", name="print1")
247    public static <ELEM> String printListAbbrev(
248            Iterable<ELEM>                      list,
249            IntTFunction<? super ELEM, String>  listItemPrinter,
250            int                                 lineWidth,
251            int                                 indentation,
252            boolean                             seeEscapedNewLinesAsText,
253            boolean                             printNulls,
254            boolean                             showLineNumbers
255        )
256    {
257        return PrintListAbbrev.print1(
258            list, listItemPrinter, lineWidth, indentation, seeEscapedNewLinesAsText,
259            printNulls, showLineNumbers
260        );
261    }
262
263    /**
264     * Convenience Method.
265     * <BR />Passes: {@code Object.toString()} to {@code 'listItemPrinter'}
266     * <BR />See Documentation: {@link #printListAbbrev(Object[], IntTFunction, int, int, boolean,
267     *      boolean, boolean)}
268     */
269    @LinkJavaSource(handle="PrintListAbbrev", name="print2")
270    public static <ELEM> String printListAbbrev(
271            ELEM[]  arr,
272            int     lineWidth,
273            int     indentation,
274            boolean seeEscapedNewLinesAsText,
275            boolean printNulls,
276            boolean showLineNumbers
277        )
278    {
279        return PrintListAbbrev.print2(
280            arr, (int i, Object o) -> o.toString(), lineWidth, indentation,
281            seeEscapedNewLinesAsText, printNulls, showLineNumbers
282        );
283    }
284
285    /**
286     * <EMBED CLASS=defs DATA-LIST_TYPE=Array>
287     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_DESCRIPTION>
288     * @param list Any iterable-list of Java Object's
289     * 
290     * @param listItemPrinter
291     * <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_LIST_ITEM_PR>
292     * 
293     * @param lineWidth                 <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_LINE_WIDTH>
294     * @param indentation               <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_INDENTATION>
295     * @param seeEscapedNewLinesAsText  <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_SEE_ESC_NL>
296     * @param printNulls                <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_PRINT_NULLS>
297     * @param showLineNumbers           <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_SHOW_LNUMS>
298     * @return                          <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_RETURNS>
299     * 
300     * @see #zeroPad10e2(int)
301     * @see #abbrevEndRDSF(String, int, boolean)
302     * @see StringParse#trimLeft(String)
303     */
304    @LinkJavaSource(handle="PrintListAbbrev", name="print2")
305    public static <ELEM> String printListAbbrev(
306            ELEM[]                              list,
307            IntTFunction<? super ELEM, String>  listItemPrinter,
308            int                                 lineWidth,
309            int                                 indentation,
310            boolean                             seeEscapedNewLinesAsText,
311            boolean                             printNulls,
312            boolean                             showLineNumbers
313        )
314    {
315        return PrintListAbbrev.print2(
316            list, listItemPrinter, lineWidth, indentation, seeEscapedNewLinesAsText,
317            printNulls, showLineNumbers
318        );
319    }
320
321
322    // ********************************************************************************************
323    // ********************************************************************************************
324    // Line(s) of Text
325    // ********************************************************************************************
326    // ********************************************************************************************
327
328
329    /**
330     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_LINE_OR_DESC>
331     * 
332     * @param s This may be any valid Java {@code String}.  It ought to be some variant of a
333     * text-file. It will be searched for the nearest {@code '\n'} character - <I>in both
334     * directions, left and right</I> - from parameter {@code int 'pos'} and parameter
335     * {@code int 'len'}
336     * 
337     * @param pos This is a position in the input-parameter {@code String 's'}.  The nearest
338     * {@code '\n'} (new-line) character, <I>to the left</I> of this position, will be found and
339     * identified.  If the {@code char} at {@code s.charAt(pos)} is, itself, a {@code '\n'}
340     * (newline) character, then no left-direction search will be performed.  The left-most
341     * position of the returned substring would then be {@code pos + 1}.
342     * 
343     * @param len The search for the 'right-most' {@code '\n'} (newline-character) will begin at
344     * position {@code 'len'}.  If the character at {@code s.charAt(pos + len)} is, itself, a 
345     * new-line character, then no right-direction search will be performed.  The right-most
346     * position of the returned substring would be {@code pos + len - 1}.
347     * 
348     * @param unixColorCode If this {@code String} is null, it will be ignored.  If this
349     * {@code String} is non-null, it will be inserted before the "Matching {@code String}"
350     * indicated by the index-boundaries {@code pos} <I>TO</I> {@code pos + len}.
351     * 
352     * <BR /><BR /><DIV CLASS=JDHint>
353     * <B STYLE='color:red;'>Note:</B> No Validity Check shall be performed on this {@code String},
354     * and the user is not obligated to provide a {@link C} valid UNIX Color-Code {@code String}.
355     * Also, a closing {@code C.RESET} is inserted after the terminus of the match.
356     * </DIV>
357     * 
358     * @return The {@code String} demarcated by the first new-line character PLUS 1
359     * <I><B>BEFORE</I></B> index {@code 'pos'}, and the first new-line character MINUS 1
360     * <I><B>AFTER</I></B> index {@code pos + len}.
361     * 
362     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_LINES_RET>
363     * 
364     * @throws StringIndexOutOfBoundsException If either {@code 'pos'}, or {@code 'pos + len'} are
365     * not within the bounds of the input {@code String 's'}
366     * 
367     * @throws IllegalArgumentException If the value passed to parameter {@code 'len'} is zero or
368     * negative.
369     */
370    public static String lineOrLines(String s, int pos, int len, String unixColorCode)
371    {
372        if ((pos >= s.length()) || (pos < 0)) throw new StringIndexOutOfBoundsException(
373            "The integer passed to parameter 'pos' [" + pos + "], is past the bounds of the end " +
374            "of String 's', which has length [" + s.length() + "]"
375        );
376
377        if (len <= 0) throw new IllegalArgumentException
378            ("The value passed to parameter 'len' [" + len + "], may not be negative.");
379
380        if ((pos + len) > s.length()) throw new StringIndexOutOfBoundsException(
381            "The total of parameter 'pos' [" + pos + "], and parameter 'len' [" + len + "], is: " +
382            "[" + (pos + len) + "].  Unfortunately, String parameter 's' only has length " +
383            "[" + s.length() + "]"
384        );
385
386        int linesStart, linesEnd, temp;
387
388        if (pos == 0)                                           linesStart  = 0;
389        else if (s.charAt(pos) == '\n')                         linesStart  = pos + 1; 
390        else if ((temp = StrIndexOf.left(s, pos, '\n')) != -1)  linesStart  = temp + 1;
391        else                                                    linesStart  = 0;
392
393        if ((pos + len) == s.length())                          linesEnd    = s.length();
394        else if (s.charAt(pos + len) == '\n')                   linesEnd    = pos + len;
395        else if ((temp = s.indexOf('\n', pos + len)) != -1)     linesEnd    = temp;
396        else                                                    linesEnd    = s.length();
397
398        /*
399        // VERY USEFUL FOR DEBUGGING.  DO NOT DELETE...
400        // NOTE: This method is the one that GREP uses.
401        System.out.println("s.charAt(pos)\t\t= "    + "[" + s.charAt(pos) + "]");
402        System.out.println("s.charAt(pos+len)\t= "  + "[" + s.charAt(pos+len) + "]");
403        System.out.println("s.length()\t\t= "       + s.length());
404        System.out.println("pos\t\t\t= "            + pos);
405        System.out.println("pos + len\t\t= "        + (pos + len));
406        System.out.println("linesStart\t\t= "       + linesStart);
407        System.out.println("linesEnd\t\t= "         + linesEnd);
408        */
409
410        return  (unixColorCode != null)
411            ?   s.substring(linesStart, pos) + 
412                unixColorCode + s.substring(pos, pos + len) + RESET + 
413                s.substring(pos + len, linesEnd)
414            :   s.substring(linesStart, linesEnd);
415
416                /*
417                OOPS.... For Posterity, this shall remain, here, but commented
418                s.substring(linesStart, pos) + 
419                s.substring(pos, pos + len) + 
420                s.substring(pos + len, linesEnd);
421                */
422    }
423
424    /**
425     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_LINE_DESC>
426     * 
427     * @param s This may be any valid Java {@code String}.  It ought to be some variant of a
428     * text-file. It will be searched for the nearest {@code '\n'} character - <I>in both
429     * directions, left and right</I> - from parameter {@code int 'pos'}.
430     * 
431     * @param pos This is a position in the input-parameter {@code String 's'}.  The nearest
432     * new-line character both to the left of this position, and to the right, will be found and
433     * identified. If the character at {@code s.charAt(pos)} is itself a newline {@code '\n'}
434     * character, then <I>an exception shall throw</I>.
435     * 
436     * @return The {@code String} identified by the first new-line character PLUS 1
437     * <I><B>BEFORE</I></B> index {@code 'pos'}, and the first new-line character MINUS 1
438     * <I><B>AFTER</I></B> index {@code 'pos + len'}.
439     * 
440     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_LINES_RET>
441     * 
442     * @throws StringIndexOutOfBoundsException If {@code 'pos'} is not within the bounds of the 
443     * input {@code String 's'}
444     * 
445     * @throws IllegalArgumentException If the character in {@code String 's'} at position
446     * {@code 'pos'} is a newline {@code '\n'}, itself.
447     */
448    public static String line(String s, int pos)
449    {
450        if ((pos > s.length()) || (pos < 0)) throw new StringIndexOutOfBoundsException(
451            "The integer passed to parameter 'pos' [" + pos + "], is past the bounds of the end of " +
452            "String 's', which has length [" + s.length() + "]"
453        );
454
455        if (s.charAt(pos) == '\n') throw  new IllegalArgumentException(
456            "The position-index for string-parameter 's' contains, itself, a new line character " +
457            "'\\n.'  This is not allowed here."
458        );
459
460        int lineStart, lineEnd;
461
462        // Prevents StrIndexOf from throwing StringINdexOutOfBounds
463        if (pos == 0) lineStart = 0;
464
465        // Also prevent lineStart equal-to '-1'
466        else if ((lineStart = StrIndexOf.left(s, pos, '\n')) == -1) lineStart = 0;
467
468        // Prevent lineEnd equal to '-1'
469        if ((lineEnd = s.indexOf('\n', pos)) == -1) lineEnd = s.length();
470
471        // if this is the first line, there was no initial '\n', so don't skip it!
472        return (lineStart == 0)
473
474            // This version returns the String from the position-0 (Pay Attention!)
475            ? s.substring(0, lineEnd)
476
477            // This version simply eliminates the '\n' that is in the directly-preceeding character
478            : s.substring(lineStart + 1, lineEnd);
479    }
480
481    /**
482     * This will retrieve the first {@code 'n'} lines of a {@code String} - where a line is defined
483     * as everything up to and including the next newline {@code '\n'} character.
484     * 
485     * @param s Any java {@code String}.
486     * 
487     * @param n This is the number of lines of text to retrieve.
488     * 
489     * @return a substring of s where the last character in the {@code String} is a {@code '\n'}.
490     * The last character should be the nth {@code '\n'} character found in s.  If there is no such
491     * character, then the original {@code String} shall be returned instead.
492     * 
493     * @throws NException This exception shall throw if parameter {@code 'n'} is less than 1, or
494     * longer than {@code s.length()}.
495     */
496    public static String firstNLines(String s, int n)
497    {
498        NException.check(n, s);
499        int pos = StrIndexOf.nth(s, n, '\n');
500
501        if (pos != -1)  return s.substring(0, pos + 1);
502        else            return s;
503    }
504
505    /**
506     * This will retrieve the last 'n' lines of a {@code String} - where a line is defined as
507     * everything up to and including the next newline {@code '\n'} character.
508     * 
509     * @param s Any java {@code String}.
510     * 
511     * @param n This is the number of lines of text to retrieve.
512     * 
513     * @return a substring of {@code 's'} where the last character in the {@code String} is a
514     * new-line character {@code '\n'}, and the first character is the character directly before
515     * the nth newline {@code '\n'} found in {@code 's'} - starting the count at the end of the
516     * {@code String}.  If there is no such substring, then the original {@code String} shall be
517     * returned.
518     * 
519     * @throws NException This exception shall throw if {@code 'n'} is less than 1, or longer
520     * {@code s.length()}.
521     */
522    public static String lastNLines(String s, int n)
523    {
524        NException.check(n, s);
525        int pos = StrIndexOf.nthFromEnd(s, n, '\n');
526
527        if (pos != -1)  return s.substring(pos + 1);
528        else            return s;
529    }
530
531    /**
532     * This is used for "trimming each line" of an input {@code String}.  Generally, when dealing
533     * with HTML there may be superfluous white-space that is useful in some places, but not
534     * necessarily when HTML is copied and pasted to other sections of a page (or to another page,
535     * altogether).  This will split a {@code String} by new-line characters, and then trim each
536     * line, and afterward rebuild the {@code String} and return it. 
537     * 
538     * <BR /><BR /><DIV CLASS=JDHint>
539     * <B STYLE='color:red;'>CRLF Issues:</B> This will only split the {@code String} using the
540     * standard {@code '\n'} character.  If the {@code String} being used uses {@code '\r'} or
541     * {@code '\n\r'}, use a different trim method.
542     * </DIV>
543     * 
544     * @param str This may be any {@code String}.  It will be split by new-line characters
545     * {@code '\n'}
546     * 
547     * @return Returns the rebuilt {@code String}, with each line having a {@code String.trim();}
548     * operation performed.
549     */
550    public static String trimEachLine(String str)
551    {
552        StringBuilder sb = new StringBuilder();
553
554        for (String s : str.split("\\n"))
555
556            if ((s = s.trim()).length() == 0)   continue;
557            else                                sb.append(s + '\n');
558
559        return sb.toString().trim();
560    }
561
562    /**
563     * Interprets an input {@code String} as one which was read out of a Text-File.  Counts the
564     * number of new-line ({@code '\n'}) characters between {@code String} indices {@code '0'} and
565     * {@code 'pos'}
566     * 
567     * <BR /><BR />This is intended be the Line-Number where {@code String}-Index parameter
568     * {@code 'pos'} is located inside the {@code 'str'} (presuming {@code 'str'} was retrieved
569     * from a Text-File).
570     * 
571     * @param str Any Java {@code String}, preferably one with multiple lines of text.
572     * @param pos Any valid {@code String}-Index that occurs ithin {@code 'str'}
573     * 
574     * @return The Line-Number within Text-File parameter {@code 'str'} which contains
575     * {@code String}-Index parameter {@code 'pos'}
576     * 
577     * @throws StringIndexOutOfBoundsException If integer-parameter {@code 'pos'} is negative or
578     * past the length of the {@code String}-Parameter {@code 'str'}.
579     * 
580     * @see #lineNumberSince(String, int, int, int)
581     */
582    public static int lineNumber(String str, int pos)
583    {
584        if (pos < 0) throw new StringIndexOutOfBoundsException
585            ("The number provided to index parameter 'pos' : [" + pos + "] is negative.");
586
587        if (pos >= str.length()) throw new StringIndexOutOfBoundsException(
588            "The number provided to index parameter 'pos' : [" + pos + "] is greater than the " +
589            "length of the input String-Parameter 'str' [" + str.length() + "]."
590        );
591
592        int lineNum = 1;
593
594        for (int i=0; i <= pos; i++) if (str.charAt(i) == '\n') lineNum++;
595
596        return lineNum;
597    }
598
599    /**
600     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_LINE_SINCE_DESC>
601     * 
602     * @param str Any Java {@code String}.  This {@code String} will be interpreted as a Text-File
603     * whose newline characters ({@code '\n'} chars) represent lines of text.
604     * 
605     * @param pos Any valid {@code String}-index within {@code 'str'}
606     * 
607     * @param prevLineNum This should be the Line-Number that contains the {@code String}-index
608     * {@code 'prevPos'}
609     * 
610     * @param prevPos This may be any index contained by {@code String} parameter {@code 'str'}.
611     * It is expected that this parameter be an index that occured on Line-Number
612     * {@code 'prevLineNum'} of the Text-File {@code 'str'}
613     * 
614     * @return The Line-Number within Text-File parameter {@code 'str'} that contains
615     * {@code String}-index {@code 'pos'}
616     * 
617     * @throws IllegalArgumentException If {@code 'pos'} is less than or equal to
618     * {@code 'prevPos'}, or if {@code 'prevLineNum'} is less than zero.
619     * 
620     * @see #lineNumber(String, int)
621     */
622    public static int lineNumberSince(String str, int pos, int prevLineNum, int prevPos)
623    {
624        if (pos <= prevPos) throw new IllegalArgumentException(
625            "The number provided to index parameter 'pos' : [" + pos + "] is less than or equal " +
626            "to previous-match index-parameter prevPos : [" + prevPos + "]"
627        );
628
629        if (prevLineNum < 0) throw new IllegalArgumentException(
630            "You have provided a negative number to Line-Number parameter 'prevLineNum' : " +
631            "[" + prevLineNum + "]"
632        );
633
634        for (int i = (prevPos + 1); i <= pos; i++) if (str.charAt(i) == '\n') prevLineNum++;
635
636        return prevLineNum;
637    }
638
639
640    // ********************************************************************************************
641    // ********************************************************************************************
642    // Abbreviation: Line-Length **AND** Number of Lines
643    // ********************************************************************************************
644    // ********************************************************************************************
645
646
647    /**
648     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WHA_DESCRIPTION>
649     * 
650     * @param s                 Any Java {@code String}
651     * @param horizAbbrevStr    <EMBED CLASS='external-html' DATA-FILE-ID=SP_WHA_H_ABBREV_STR>
652     * @param maxLineLength     <EMBED CLASS='external-html' DATA-FILE-ID=SP_WHA_MAX_LINE_LEN>
653     * @param maxNumLines       <EMBED CLASS='external-html' DATA-FILE-ID=SP_WHA_MAX_NM_LINES>
654     * 
655     * @param compactConsecutiveBlankLines When this parameter is passed {@code TRUE}, any 
656     * series of Empty-Lines, or lines only containing White-Space will be compacted to a single 
657     * line of text that simply states {@code "[Compacted 10 Blank Lines]"} (or however many 
658     * White-Space-Only Lines were actually compacted, if that number isn't {@code '10'})
659     * 
660     * @return                          The modified {@code String}.
661     * @throws IllegalArgumentException <EMBED CLASS='external-html' DATA-FILE-ID=SP_WHA_IAEX>
662     */
663    @LinkJavaSource(handle="VertAndHorizAbbrev")
664    public static String widthHeightAbbrev(
665            final String    s,
666            final String    horizAbbrevStr,
667            final Integer   maxLineLength,
668            final Integer   maxNumLines,
669            final boolean   compactConsecutiveBlankLines
670        )
671    {
672        return VertAndHorizAbbrev.print
673            (s, horizAbbrevStr, maxLineLength, maxNumLines, compactConsecutiveBlankLines);
674    }
675
676
677    // ********************************************************************************************
678    // ********************************************************************************************
679    // Wrap Text to another line
680    // ********************************************************************************************
681    // ********************************************************************************************
682
683
684    /**
685     * Convenience Method.
686     * <BR />See Documentation: {@link #wrap(String, int, int)}
687     * <BR />Passes same {@code 'lineLen'} value to <B><I>BOTH</I></B> Line-Length Parameters
688     */
689    @LinkJavaSource(handle="Wrap")
690    @LinkJavaSource(handle="WrapHelpers")
691    public static String wrap(String s, int lineLen)
692    { return Wrap.wrap(s, lineLen, lineLen); }
693
694    /**
695     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_DESCRIPTION>
696     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_RIGHT_TRIM>
697     * 
698     * @param s             Any Java {@code String}; may not contain {@code '\t', '\f' or '\r'}
699     * @param firstLineLen  <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_1ST_LN_LEN>
700     * @param lineLen       The maximum allowable Line-Length for all other lines of text.
701     * @return              <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_RETURNS>
702     * 
703     * @throws StringFormatException <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_STR_FMT_EX>
704     * @throws IllegalArgumentException If {@code 'firstLineLen'} or {@code 'lineLen'} are
705     * zero or negative.
706     */
707    @LinkJavaSource(handle="Wrap")
708    @LinkJavaSource(handle="WrapHelpers")
709    public static String wrap(String s, int firstLineLen, int lineLen)
710    { return Wrap.wrap(s, firstLineLen, lineLen); }
711 
712
713    /**
714     * Convenience Method.
715     * <BR />See Documentation: {@link #wrapToIndentation(String, int, int)}
716     * <BR />Passes same {@code 'lineLen'} value to <B><I>BOTH</I></B> Line-Length Parameters
717     */
718    @LinkJavaSource(handle="WrapToIndentation")
719    @LinkJavaSource(handle="WrapHelpers")
720    public static String wrapToIndentation(String s, int lineLen)
721    { return WrapToIndentation.wrap(s, lineLen, lineLen); }
722
723    /**
724     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_DESCRIPTION>
725     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_RIGHT_TRIM>
726     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_TO_INDENT>
727     * 
728     * @param s             Any Java {@code String}; may not contain {@code '\t', '\f' or '\r'}
729     * @param firstLineLen  <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_1ST_LN_LEN>
730     * @param lineLen       The maximum allowable Line-Length for all other lines of text.
731     * @return              <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_RETURNS>
732     * 
733     * <BR /><BR />As already explained, the returned {@code String} shall contain "wrapped" text 
734     * which has an amount of indentation (space characters) equal to whatever line is directly
735     * above it.  Indented lines, effectively, "inherit" the amount of indentation present in the 
736     * line coming directly before them.
737     * 
738     * @throws StringFormatException <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_STR_FMT_EX>
739     * @throws IllegalArgumentException If {@code 'firstLineLen'} or {@code 'lineLen'} are
740     * zero or negative.
741     */
742    @LinkJavaSource(handle="WrapToIndentation")
743    @LinkJavaSource(handle="WrapHelpers")
744    public static String wrapToIndentation(String s, int firstLineLen, int lineLen)
745    { return WrapToIndentation.wrap(s, firstLineLen, lineLen); }
746
747    /**
748     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_DESCRIPTION>
749     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_RIGHT_TRIM>
750     * 
751     * @param s             Any Java {@code String}; may not contain {@code '\t', '\f' or '\r'}
752     * @param firstLineLen  <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_1ST_LN_LEN>
753     * @param lineLen       The maximum allowable Line-Length for all other lines of text.
754     * 
755     * @param extraSpaces   Wrapped text will get an additional amount of indentation, past 
756     *                      any and all inherited indentation
757     * @return Wrapped.
758     * 
759     * @throws StringFormatException <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_STR_FMT_EX>
760     * @throws IllegalArgumentException If {@code 'firstLineLen'} or {@code 'lineLen'} are
761     * zero or negative.
762     */
763    @LinkJavaSource(handle="WrapToIndentationPlus")
764    @LinkJavaSource(handle="WrapHelpers")
765    public static String wrapToIndentationPlus
766        (String s, int firstLineLen, int lineLen, int extraSpaces)
767    { return WrapToIndentationPlus.wrap(s, firstLineLen, lineLen, extraSpaces); }
768
769
770    // ********************************************************************************************
771    // ********************************************************************************************
772    // Misc Date String Functions
773    // ********************************************************************************************
774    // ********************************************************************************************
775
776
777    private static final Calendar internalCalendar = Calendar.getInstance();
778
779    /**
780     * Generates a "Date String" using the character separator {@code '.'}  
781     * @return A {@code String} in the form: {@code YYYY.MM.DD}
782     */
783    public static String dateStr() { return dateStr('.', false); }
784
785    /**
786     * Generates a "Date String" using the <I>separator</I> parameter as the separator between
787     * numbers
788     * 
789     * @param separator Any ASCII or UniCode character.
790     * 
791     * @return A {@code String} of the form: {@code YYYYcMMcDD} where {@code 'c'} is the passed
792     * {@code 'separator'} parameter.
793     */
794    public static String dateStr(char separator) { return dateStr(separator, false); }
795
796    /**
797     * Generates a "Date String" that is consistent with the directory-name file-storage locations
798     * used to store articles from {@code http://Gov.CN}.
799     * 
800     * @return The {@code String's} used for the Chinese Government Web-Portal Translation Pages
801     */
802    public static String dateStrGOVCN() { return dateStr('/', false).replaceFirst("/", "-"); }
803    // "2017-12/05"
804
805    /**
806     * This class is primary included because although Java has a pretty reasonable "general
807     * purpose" calendar class/interface, but a consistent / same {@code String} since is needed
808     * because the primary use here is for building the names of files.
809     *
810     * @param separator Any ASCII or Uni-Code character.
811     * 
812     * @param includeMonthName When <I>TRUE</I>, the English-Name of the month ({@code 'January'}
813     * ... {@code 'December'}) will be appended to the month number in the returned {@code String}.
814     * 
815     * @return The year, month, and day as a {@code String}.
816     */
817    public static String dateStr(char separator, boolean includeMonthName)
818    {
819        Calendar    c   = internalCalendar;
820        String      m   = zeroPad10e2(c.get(Calendar.MONTH) + 1); // January is month zero!
821        String      d   = zeroPad10e2(c.get(Calendar.DAY_OF_MONTH));
822
823        if (includeMonthName) m += " - " + c.getDisplayName(Calendar.MONTH, 2, Locale.US);
824
825        return (separator != 0)
826            ? (c.get(Calendar.YEAR) + "" + separator + m + separator + d)
827            : (c.get(Calendar.YEAR) + "" + m + d);
828    }
829
830
831    // ********************************************************************************************
832    // ********************************************************************************************
833    // Misc Month-Date String Functions
834    // ********************************************************************************************
835    // ********************************************************************************************
836
837
838    /** The months of the year, as an immutable list of {@code String's}. */
839    public static final ReadOnlyList<String> months = new ReadOnlyArrayList<>(
840        "January", "February", "March", "April", "May", "June",
841        "July", "August", "September", "October", "November", "December"
842    );
843
844    /**
845     * Converts an integer into a Month.
846     * @param month The month, as a number from {@code '1'} to {@code '12'}.
847     * @return A month as a {@code String} like: {@code "January"} or {@code "August"}
848     * @see #months
849     */
850    public static String monthStr(int month) { return months.get(month); }
851
852    /**
853     * Returns a {@code String} that has the year and the month (but not the day, or any other
854     * time related components).
855     *
856     * @return Returns the current year and month as a {@code String}.
857     */
858    public static String ymDateStr() { return ymDateStr('.', false); }
859
860    /**
861     * Returns a {@code String} that has the year and the month (but not the day, or other time
862     * components).
863     * 
864     * @param separator The single-character separator used between year, month and day.
865     * @return The current year and month as a {@code String}.
866     */
867    public static String ymDateStr(char separator) { return ymDateStr(separator, false); }
868
869    /**
870     * Returns a {@code String} that has the year and the month (but not the day, or other time
871     * components).
872     * 
873     * @param separator The single-character separator used between year, month and day.
874     * 
875     * @param includeMonthName When this is true, the name of the month, in English, is included
876     * with the return {@code String}.
877     * 
878     * @return {@code YYYY<separator>MM(? include-month-name)}
879     */
880    public static String ymDateStr(char separator, boolean includeMonthName)
881    {
882        Calendar    c   = internalCalendar;
883        String      m   = zeroPad10e2(c.get(Calendar.MONTH) + 1); // January is month zero!
884
885        if (includeMonthName) m += " - " + c.getDisplayName(Calendar.MONTH, 2, Locale.US);
886
887        return (separator != 0)
888            ? (c.get(Calendar.YEAR) + "" + separator + m)
889            : (c.get(Calendar.YEAR) + "" + m);
890    }
891
892
893    // ********************************************************************************************
894    // ********************************************************************************************
895    // Misc Time String Functions
896    // ********************************************************************************************
897    // ********************************************************************************************
898
899
900    /**
901     * Returns the current time as a {@code String}.
902     * 
903     * @return military time - with AM|PM (redundant) added too.
904     * Includes only Hour and Minute - separated by a colon character {@code ':'}
905     * 
906     * @see #timeStr(char)
907     */
908    public static String timeStr() { return timeStr(':'); }
909
910    /**
911     * Returns the current time as a {@code String}.
912     * @param separator The character used to separate the minute &amp; hour fields
913     * @return military time - with AM|PM added redundantly, and a separator of your choosing.
914     */
915    public static String timeStr(char separator)
916    {
917        Calendar    c   = internalCalendar;
918        int         ht  = c.get(Calendar.HOUR) + ((c.get(Calendar.AM_PM) == Calendar.AM) ? 0 : 12);
919        String      h   = zeroPad10e2((ht == 0) ? 12 : ht);  // 12:00 is represented as "0"... changes this...
920        String      m   = zeroPad10e2(c.get(Calendar.MINUTE));
921        String      p   = (c.get(Calendar.AM_PM) == Calendar.AM) ? "AM" : "PM";
922
923        return (separator != 0)
924            ? (h + separator + m + separator + p)
925            : (h + m + p);
926    }
927
928    /**
929     * Returns the current time as a {@code String}.  This method uses all time components
930     * available.
931     * 
932     * @return military time - with AM|PM added redundantly.
933     */
934    public static String timeStrComplete()
935    {
936        Calendar    c   = internalCalendar;
937        int         ht  = c.get(Calendar.HOUR) + ((c.get(Calendar.AM_PM) == Calendar.AM) ? 0 : 12);
938        String      h   = zeroPad10e2((ht == 0) ? 12 : ht);  // 12:00 is represented as "0"
939        String      m   = zeroPad10e2(c.get(Calendar.MINUTE));
940        String      s   = zeroPad10e2(c.get(Calendar.SECOND));
941        String      ms  = zeroPad(c.get(Calendar.MILLISECOND));
942        String      p   = (c.get(Calendar.AM_PM) == Calendar.AM) ? "AM" : "PM";
943
944        return h + '-' + m + '-' + p + '-' + s + '-' + ms + "ms";
945    }
946
947
948    // ********************************************************************************************
949    // ********************************************************************************************
950    // More Numeric Methods
951    // ********************************************************************************************
952    // ********************************************************************************************
953
954
955    private static final DecimalFormat formatter = new DecimalFormat("#,###");
956
957    /**
958     * Makes a {@code long} number like {@code 123456789} into a number-string such as:
959     * {@code "123,456,789"}. Java's {@code package java.text.*} is easy to use, and versatile, but
960     * the commands are not always so easy to remember.
961     *
962     * @param l Any {@code long} integer.  Comma's will be inserted for every third power of ten
963     * 
964     * @return After calling java's {@code java.text.DecimalFormat} class, a {@code String}
965     * representing this parameter will be returned.
966     */
967    public static String commas(long l)
968    { return formatter.format(l); }
969
970    /**
971     * The words "ordinal indicator" are referring to the little character {@code String} that is
972     * often used in English to make a number seem more a part of an english sentence.
973     * 
974     * @param i Any positive integer (greater than 0)
975     *
976     * @return This will return the following strings:
977     * 
978     * <TABLE CLASS=JDBriefTable>
979     * <TR><TH>Input:   </TH><TH>RETURNS:</TH></TR>
980     * <TR><TD>i = 1    </TD><TD>"st" &nbsp;(as in "1st","first")   </TD></TR>
981     * <TR><TD>i = 2    </TD><TD>"nd" &nbsp;(as in "2nd", "second") </TD></TR>
982     * <TR><TD>i = 4    </TD><TD>"th" &nbsp;(as in "4th")           </TD></TR>
983     * <TR><TD>i = 23   </TD><TD>"rd" &nbsp;(as in "23rd")          </TD></TR>
984     * </TABLE>
985     * 
986     * @throws IllegalArgumentException If i is negative, or zero
987     */
988    public static String ordinalIndicator(int i)
989    {
990        if (i < 1) throw new IllegalArgumentException
991            ("i: " + i + "\tshould be a natural number > 0.");
992
993
994        // Returns the last 2 digits of the number, or the number itself if it is less than 100.
995        // Any number greater than 100 - will not have the "text-ending" (1st, 2nd, 3rd..) affected
996        // by the digits after the first two digits.  Just analyze the two least-significant digits
997
998        i = i % 100;
999
1000        // All numbers between "4th" and "19th" end with "th"
1001        if ((i > 3) && (i < 20))    return "th";
1002
1003        // set i to be the least-significant digit of the number - if that number was 1, 2, or 3
1004        i = i % 10;
1005
1006        // Obvious: English Rules.
1007        if (i == 1) return "st";
1008        if (i == 2) return "nd";
1009        if (i == 3) return "rd";
1010
1011        // Compiler is complaining.  This statement should never be executed.
1012        return "th";
1013    }
1014
1015
1016    // ********************************************************************************************
1017    // ********************************************************************************************
1018    // Zero Padding stuff
1019    // ********************************************************************************************
1020    // ********************************************************************************************
1021
1022
1023    /**
1024     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_ZERO_PAD_DESC>
1025     * @param n Any Integer.  If {@code 'n'} is negative or greater than 1,000 - null is returned.
1026     * @return <EMBED CLASS='external-html' DATA-FILE-ID=SP_ZERO_PAD_RET>
1027     * @see #zeroPad10e2(int)
1028     * @see #zeroPad10e4(int)
1029     */
1030    public static String zeroPad(int n)
1031    {
1032        if (n < 0)      return null;
1033        if (n < 10)     return "00" + n;
1034        if (n < 100)    return "0" + n;
1035        if (n < 1000)   return "" + n;
1036        return null;
1037    }
1038
1039    /**
1040     * Pads an integer such that it contains enough leading zero's to ensure a
1041     * {@code String}-length of two.
1042     * 
1043     * @param n Must be an integer between 0 and 99, or else null will be returned
1044     * @return <EMBED CLASS='external-html' DATA-FILE-ID=SP_ZP_10E2_RET>
1045     * @see #zeroPad(int)
1046     */
1047    public static String zeroPad10e2(int n)
1048    {
1049        if (n < 0)      return null;
1050        if (n < 10)     return "0" + n;
1051        if (n < 100)    return "" + n;
1052        return null;
1053    }
1054
1055    /**
1056     * Pads an integer such that it contains enough leading zero's to ensure a String-length of
1057     * four.
1058     * 
1059     * @param n Must be an integer between 0 and 9999, or else null will be returned
1060     * @return <EMBED CLASS='external-html' DATA-FILE-ID=SP_ZP_10E4_RET>
1061     * @see #zeroPad(int)
1062     */
1063    public static String zeroPad10e4(int n)
1064    {
1065        if (n < 0)      return null;
1066        if (n < 10)     return "000" + n;
1067        if (n < 100)    return "00" + n;
1068        if (n < 1000)   return "0" + n;
1069        if (n < 10000)  return "" + n;
1070        return null;
1071    }
1072
1073    /**
1074     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_ZP_POW2_DESC>
1075     * 
1076     * @param n Must be an integer between {@code '0'} and {@code '9999'} where the number of 
1077     * {@code '9'} digits is equal to the value of parameter {@code int 'powerOf10'}
1078     * 
1079     * @param powerOf10 This must be a positive integer greater than {@code '1'}.  It may not be 
1080     * larger {@code '11'}.  The largest value that any integer in Java may attain is
1081     * {@code '2,147,483, 647'}
1082     * 
1083     * @return <EMBED CLASS='external-html' DATA-FILE-ID=SP_ZP_POW2_RET>
1084     * 
1085     * @throws IllegalArgumentException if the value parameter {@code 'powerOf10'} is less than 2,
1086     * or greater than {@code 11}.
1087     */
1088    public static String zeroPad(int n, int powerOf10)
1089    {
1090        if (n < 0) return null;                 // Negative Values of 'n' not allowed
1091
1092        char[]  cArr    = new char[powerOf10];  // The String's length will be equal to 'powerOf10'
1093        String  s       = "" + n;               //       (or else 'null' would be returned)
1094        int     i       = powerOf10 - 1;        // Internal Loop variable
1095        int     j       = s.length() - 1;       // Internal Loop variable
1096
1097        Arrays.fill(cArr, '0');                 // Initially, fill the output char-array with all
1098                                                // zeros
1099
1100        while ((i >= 0) && (j >= 0))            // Now start filling that char array with the
1101            cArr[i--] = s.charAt(j--);          // actual number
1102
1103        if (j >= 0) return null;                // if all of parameter 'n' was inserted into the
1104                                                // output (number 'n' didn't fit) then powerOf10
1105                                                // was insufficient, so return null.
1106
1107        return new String(cArr);
1108    }
1109
1110}