001package Torello.Java;
002
003import Torello.Java.Function.IntTFunction;
004import static Torello.Java.C.*;
005
006// Needed for a few JavaDoc Comments
007import Torello.JavaDoc.Field;
008import Torello.JavaDoc.Constructor;
009import Torello.JavaDoc.Method;
010import Torello.JavaDoc.AnnotationElem;
011
012import Torello.JavaDoc.LinkJavaSource;
013import static Torello.JavaDoc.Entity.METHOD;
014
015/**
016 * This class provides several {@code String} printing utilities such as abbreviation and list
017 * printing.
018 */
019@Torello.JavaDoc.StaticFunctional
020public class StrPrint
021{
022    private StrPrint() { }
023
024
025    // ********************************************************************************************
026    // ********************************************************************************************
027    // HELPER & BASIC
028    // ********************************************************************************************
029    // ********************************************************************************************
030
031
032    /**
033     * Converts every line-character ({@code '\n'}) - <I>and any white-space before or after
034     * that character</I>, into the {@code String} - {@code "\\n"} - which is the actual two
035     * character sequence of a back-slash ({@code '\'}), followed by the letter {@code 'n'}.
036     * 
037     * <BR /><BR />After new-line characters are replaced, the method will remove any duplicate
038     * spaces that are present in the {@code String}, and reduce them to a single space character
039     * 
040     * <BR /><TABLE CLASS=JDBriefTable>
041     * <TR><TH>Input {@code String}</TH><TH>Returned {@code String}</TH></TR>
042     * <TR><TD>"Hello World"</TD><TD>"Hello World"</TD></TR>
043     * <TR><TD>"Hello &nbsp;&nbsp;\t &nbsp;&nbsp;World"</TD><TD>"Hello World"</TD></TR>
044     * <TR><TD>"Hello World\n"</TD><TD>"Hello World\\n"</TD></TR>
045     * <TR><TD>"Hello &nbsp;&nbsp;World &nbsp;\n\t &nbsp;\n"</TD><TD>"Hello World\\n\\n"</TD></TR>
046     * <TR>
047     *      <TD>"Hello Today!\nHow Are You?"</TD>
048     *      <TD>"Hello Today!\\nHow Are You?"</TD>
049     * </TR>
050     * <TR>
051     *      <TD>"Hello,\n &nbsp;&nbsp;Testing &nbsp;&nbsp;1, &nbsp;2, &nbsp;3\n"</TD>
052     *      <TD>"Hello,\\nTesting 1, 2, 3\\n"</TD>
053     * </TR>
054     * <TR>
055     *      <TD>"Hello,\n &nbsp;&nbsp;Testing 1, 2, 3 &nbsp;&nbsp;\n\t\t\t"</TD>
056     *      <TD>"Hello,\\nTesting 1, 2, 3\\n"</TD>
057     * </TR>
058     * <TR><TD>"\n"</TD><TD>"\\n"</TD></TR>
059     * <TR><TD>"\n&nbsp;\t"</TD><TD>"\\n"</TD></TR>
060     * <TR><TD>"\n\t&nbsp;\n\t&nbsp;\n\t&nbsp;"</TD><TD>"\\n\\n\\n"</TD></TR>
061     * </TABLE>
062     * 
063     * <BR />This method is used in printing Java Source Code to a terminal - in an abbreviated
064     * way!  After this method is finished, java-source-as-text is actually <B><I>still</I></B>
065     * look readable, and can be printed in a table of methods on a page.  It is used in the Java
066     * Doc Upgrader tool, and makes printing up <B><I>both</I></B> method-signatures
067     * <B><I>and</I></B> method bodies quite a bit easier.
068     * 
069     * <BR /><BR />For a better understanding of the use and application of this function, please
070     * take a look at the <B>{@code 'toString'}</B> methods: <B>{@link Method#toString()},
071     * {@link Constructor#toString()}, {@link Field#toString()} and
072     * {@link AnnotationElem#toString()}</B>
073     *  
074     * <BR /><BR /><B CLASS=JDDescLabel>Regular Expressions:</B>
075     * 
076     * <BR />This method uses regular-expressions, rather performing an optimized, in-place,
077     * {@code String} replacement (such as one with a {@code for} or {@code while} loop).  This
078     * means that there is a little efficiency sacrificed in the name of brevity.
079     * 
080     * <BR /><BR />The replacement used in the {@code String.replaceAll} method was thouroughly
081     * tested.  The quadruple-backslash {@code '\n'} <I>is actually necessary!</I>  The first
082     * escape used is to communicate with the Java-Compiler, and the second round of escaping is
083     * communicating with the Regular Expression Processor.
084     * 
085     * @param s Any {@code java.lang.String}, preferably one with multiple lines of text.
086     * 
087     * @return A {@code String}, where each line of text has been "trimmed", and the two
088     * character sequence {@code "\\n"} inserted in-between each line.
089     * 
090     * @see #abbrevStartRDSF(String, int, boolean)
091     * @see #abbrevEndRDSF(String, int, boolean)
092     */
093    public static String newLinesAsText(String s)
094    {
095        return s
096            .replaceAll(
097                    // White-Space-Except-Newline, THEN newline, THEN White-SpaceExcept-Newline
098                    "[ \t\r\f\b]*\n[ \t\r\f\b]*",
099
100                    // Replace Each Occurence of that with:
101                    // == COMPILES-TO ==> "\\n" == REG-EX-READS ==> BackSlash and letter 'n'
102                    "\\\\n"
103            )
104            // == COMPILES-TO ==> "\s+" == REG-EX-READS ==> 'spaces'
105            .replaceAll("\\s+", " ")
106
107            // Don't forget about leading and trailing stuff...
108            .trim();
109    }
110
111
112    // ********************************************************************************************
113    // ********************************************************************************************
114    // Abbreviating Text, with "newLinesAsText" - Helper
115    // ********************************************************************************************
116    // ********************************************************************************************
117
118
119    /**
120     * Convenience Method.
121     * <BR /><B STYLE='color: red;'>RDSF: Remove Duplicate Spaces First</B>
122     * <BR />Invokes:    {@link StringParse#removeDuplicateSpaces(String)} 
123     * <BR />Or Invokes: {@link #newLinesAsText(String)}
124     * <BR />Finally:    {@link #abbrevStart(String, boolean, int)}
125     */
126    public static String abbrevStartRDSF
127        (String s, int maxLength, boolean seeEscapedNewLinesAsText)
128    {
129        // false is passed to 'abbrevStart' parameter 'escapeNewLines' because in both scenarios
130        // of this conditional-statement, the new-lines have already been removed by the previous
131        // method call.
132        //
133        // both 'removeDuplicateSpaces' and 'newLinesAsText' remove the new-lines
134
135        return seeEscapedNewLinesAsText
136            ? abbrevStart(newLinesAsText(s), false, maxLength)
137            : abbrevStart(StringParse.removeDuplicateSpaces(s), false, maxLength);
138    }
139
140    /**
141     * Convenience Method.
142     * <BR /><B STYLE='color: red;'>RDSF: Remove Duplicate Spaces First</B>
143     * <BR />Invokes: {@link StringParse#removeDuplicateSpaces(String)}
144     * <BR />Or Invokes: {@link #newLinesAsText(String)}
145     * <BR />Finally: {@link #abbrevEnd(String, boolean, int)}
146     */
147    public static String abbrevEndRDSF
148        (String s, int maxLength, boolean seeEscapedNewLinesAsText)
149    {
150        // false is passed to 'abbrevStart' parameter 'escapeNewLines' because in both scenarios
151        // of this conditional-statement, the new-lines have already been removed by the previous
152        // method call.
153        //
154        // both 'removeDuplicateSpaces' and 'newLinesAsText' remove the new-lines
155
156        return seeEscapedNewLinesAsText
157            ? abbrevEnd(newLinesAsText(s), false, maxLength)
158            : abbrevEnd(StringParse.removeDuplicateSpaces(s), false, maxLength);
159    }
160
161    /**
162     * Convenience Method.
163     * <BR />Passes: {@code '0'} to parameter {@code 'abbrevPos'}, forcing the abbreviation to
164     * occur at the <B>start</B> of the {@code String} (if long enough to be abbreviated)
165     * <BR />See Documentation: {@link #abbrev(String, int, boolean, String, int)}
166     */
167    @LinkJavaSource(handle="Abbrev", name="print1")
168    public static String abbrevStart(String s, boolean escNewLines, int maxLength)
169    { return Abbrev.print1(s, 0, escNewLines, null, maxLength); }
170
171    /**
172     * This will abbreviate any {@code String} using either the ellipsis ({@code '...'}), or some
173     * other use provided abbreviation-{@code String} - <I>as long as the provided {@code String}
174     * is longer than {@code 'maxLength'}.</I>  When {@code 's'} is, indeed, longer than
175     * {@code 'maxLength'} the returned-{@code String} will contain the ellipsis abbreviation
176     * beginning at {@code String}-index {@code 'abbrevPos'}.
177     *
178     * <BR /><BR />You have the option of asking that new-line characters ({@code '\n'}) be
179     * escaped into the two-character {@code String: "\\n"}.  This optional is provided so that
180     * the output may fit on a single-line, for readability purposes.  It will look somewhat like
181     * an escaped {@code JSON} file, which also substitues {@code '\n'} characters for the
182     * 'escaped' version {@code "\\n"}.  <I>Note that when this occurs, the replaced-{@code String}
183     * actually only contains two characters, <B>not three,</B> since the first back-slash your are
184     * looking right here is, itself, an escape character!</I>
185     *
186     * @param s This may be any Java (non-null) {@code String}
187     *
188     * @param abbrevPos This parameter is used to indicate where the abbreviation-{@code String}
189     * should occur - <I>if this {@code String 's'} is long enough to be abbreviated.</I>  For
190     * instance, if {@code '0'} (zero) were passed to this parameter, and {@code 's'} were longer
191     * than parameter {@code 'maxLength'}, then an ellipsis would be appended to the beginning of
192     * the returned-{@code 'String'}.  (Or, if some other {@code 'abbrevStr'} were specified, that
193     * other abbreviation would be appended to the beginning of the returned-{@code String})
194     * 
195     * @param escapeNewLines <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_ENL>
196     * @param abbrevStr <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_STR>
197     * @param maxLength <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_MXL>
198     *
199     * @return If the input {@code String} has a <I>length less-than {@code 'maxLength'}</I>, then
200     * it is returned, unchanged.  If the input contained new-line characters, and you have 
201     * requested to escape them, that replacement is performed first (which makes the original 
202     * {@code String}} longer).  Then, if the {@code String} is longer than {@code 'maxLength'},
203     * it is abbreviated, and either the default ellipsis <I>or the user-provided
204     * {@code 'abbrevStr'}</I> are inserted at location {@code 'abbrevPos'} and returned.
205     * 
206     * <BR /><BR />If, after the new-line escape-replacement, the returned-{@code String} would not
207     * be longer than {@code 'maxLength'}, then that escaped-{@code String} is returned, as is
208     * (without any elliptical-characters).
209     *
210     * @throws IllegalArgumentException If the value for {@code 'maxLength'} is negative, or if it
211     * is less than the length of the abbreviation.
212     * 
213     * <BR /><BR />Specifically, if {@code 'maxLength'} isn't even long enough to fit the abbreviation
214     * itself, then this exception will throw.
215     * 
216     * <BR /><BR />If the value passed to {@code 'abbrevPos'} is negative or longer than the value
217     * passed to {@code 'maxLength'} minus the length of the ellipsis-{@code String}, then this 
218     * exception will also throw.
219     */
220    @LinkJavaSource(handle="Abbrev", name="print1")
221    public static String abbrev(
222            String  s,
223            int     abbrevPos,
224            boolean escapeNewLines,
225            String  abbrevStr,
226            int     maxLength
227        )
228    { return Abbrev.print1(s, abbrevPos, escapeNewLines, abbrevStr, maxLength); }
229
230    /**
231     * Convenience Method.
232     * <BR />Parameter: {@code spaceBeforeAbbrev} set to {@code FALSE}
233     * <BR />Abbreviates: Default ellipsis ({@code '...'}) are placed at the end of the
234     * {@code String}
235     * <BR />See Documentation: {@link #abbrev(String, boolean, boolean, String, int)}
236     */
237    @LinkJavaSource(handle="Abbrev", name="print2")
238    public static String abbrevEnd(String s, boolean escapeNewLines, int maxLength)
239    { return Abbrev.print2(s, false, escapeNewLines, null, maxLength); }
240
241    /**
242     * This will abbreviate any {@code String} using either the ellipsis ({@code '...'}), or some
243     * other use provided abbreviation-{@code String}, if the provided {@code String} is longer 
244     * than {@code 'maxLength'}.  If the returned-{@code String} is, indeed, abbreviated then the
245     * elliptical-abbreviation {@code String} will be placed <I>at the end of the
246     * returned-{@code String}</I>
247     *
248     * <BR /><BR />You have the option of asking that new-line characters ({@code '\n'}) be
249     * escaped into the two-character {@code String: "\\n"}.  This optional is provided so that
250     * the output may fit on a single-line, for readability purposes.  It will look somewhat like
251     * an escaped {@code JSON} file, which also substitues {@code '\n'} characters for the
252     * 'escaped' version {@code "\\n"}.  <I>Note that when this occurs, the replaced-{@code String}
253     * actually only contains two characters, <B>not three,</B> since the first back-slash your are
254     * looking right here is, itself, an escape character!</I>
255     * 
256     * @param s This may be any Java (non-null) {@code String}
257     *
258     * @param spaceBeforeAbbrev This ensures that for whatever variant of ellipsis being used, the
259     * space-character is inserted directly before appending the ellipsis {@code "..."} or the
260     * user-provided {@code 'abbrevStr'}.
261     *
262     * @param escapeNewLines <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_ENL>
263     * @param abbrevStr <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_STR>
264     * @param maxLength <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_MXL>
265     *
266     * @return If the input {@code String} has a <I>length less-than {@code 'maxLength'}</I>, then
267     * it is returned, unchanged.  If the input contained new-line characters, and you have 
268     * requested to escape them, that replacement is performed first (which makes the original 
269     * {@code String}} longer).  Then, if the {@code String} is longer than {@code 'maxLength'},
270     * it is abbreviated, and either the default ellipsis <I>or the user-provided
271     * {@code 'abbrevStr'}</I> are are appended to the end and returned.
272     *
273     * @throws IllegalArgumentException If the value for {@code 'maxLength'} is negative, or if it
274     * is less than the length of the abbreviation plus the value of {@code 'spaceBeforeAbbrev'}.
275     * <BR /><BR />Specifically, if {@code 'maxLength'} isn't even long enough to fit the abbreviation
276     * itself, then this exception will throw.
277     */
278    @LinkJavaSource(handle="Abbrev", name="print2")
279    public static String abbrev(
280            String  s,
281            boolean spaceBeforeAbbrev,
282            boolean escapeNewLines,
283            String  abbrevStr,
284            int     maxLength
285        )
286    { return Abbrev.print2(s, spaceBeforeAbbrev, escapeNewLines, abbrevStr, maxLength); }
287
288
289    // ********************************************************************************************
290    // ********************************************************************************************
291    // Abbreviated List Printing
292    // ********************************************************************************************
293    // ********************************************************************************************
294
295
296    /**
297     * Convenience Method.
298     * <BR />Passes: {@code Object.toString()} to {@code 'listItemPrinter'}
299     * <BR />See Documentation: {@link #printListAbbrev(Iterable, IntTFunction, int, int, boolean,
300     *  boolean, boolean)}
301     */
302    @LinkJavaSource(handle="PrintListAbbrev", name="print1")
303    public static <ELEM> String printListAbbrev(
304            Iterable<ELEM> list, int lineWidth, int indentation, boolean seeEscapedNewLinesAsText,
305            boolean printNulls, boolean showLineNumbers
306        )
307    {
308        return PrintListAbbrev.print1(
309            list, (int i, Object o) -> o.toString(), lineWidth, indentation,
310            seeEscapedNewLinesAsText, printNulls, showLineNumbers
311        );
312    }
313
314    /**
315     * <EMBED CLASS=defs DATA-LIST_TYPE=Array>
316     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_DESCRIPTION>
317     * @param list Any iterable-list of Java Object's
318     * @param listItemPrinter <EMBED CLASS='extenal-html' DATA-FILE-ID=SP_PLA_LIST_ITEM_PR>
319     * @param lineWidth <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_LINE_WIDTH>
320     * @param indentation <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_INDENTATION>
321     * @param seeEscapedNewLinesAsText <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_SEE_ESC_NL>
322     * @param printNulls <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_PRINT_NULLS>
323     * @param showLineNumbers <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_SHOW_LNUMS>
324     * @return <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_RETURNS>
325     * @see StringParse#zeroPad10e2(int)
326     * @see #abbrevEndRDSF(String, int, boolean)
327     * @see StringParse#nChars(char, int)
328     * @see StringParse#trimLeft(String)
329     */
330    @LinkJavaSource(handle="PrintListAbbrev", name="print1")
331    public static <ELEM> String printListAbbrev(
332            Iterable<ELEM>                      list,
333            IntTFunction<? super ELEM, String>  listItemPrinter,
334            int                                 lineWidth,
335            int                                 indentation,
336            boolean                             seeEscapedNewLinesAsText,
337            boolean                             printNulls,
338            boolean                             showLineNumbers
339        )
340    {
341        return PrintListAbbrev.print1(
342            list, listItemPrinter, lineWidth, indentation, seeEscapedNewLinesAsText,
343            printNulls, showLineNumbers
344        );
345    }
346
347    /**
348     * Convenience Method.
349     * <BR />Passes: {@code Object.toString()} to {@code 'listItemPrinter'}
350     * <BR />See Documentation: {@link #printListAbbrev(Object[], IntTFunction, int, int, boolean,
351     *      boolean, boolean)}
352     */
353    @LinkJavaSource(handle="PrintListAbbrev", name="print2")
354    public static <ELEM> String printListAbbrev(
355            ELEM[]  arr,
356            int     lineWidth,
357            int     indentation,
358            boolean seeEscapedNewLinesAsText,
359            boolean printNulls,
360            boolean showLineNumbers
361        )
362    {
363        return PrintListAbbrev.print2(
364            arr, (int i, Object o) -> o.toString(), lineWidth, indentation,
365            seeEscapedNewLinesAsText, printNulls, showLineNumbers
366        );
367    }
368
369    /**
370     * <EMBED CLASS=defs DATA-LIST_TYPE=Array>
371     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_DESCRIPTION>
372     * @param list Any iterable-list of Java Object's
373     * @param listItemPrinter <EMBED CLASS='extenal-html' DATA-FILE-ID=SP_PLA_LIST_ITEM_PR>
374     * @param lineWidth <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_LINE_WIDTH>
375     * @param indentation <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_INDENTATION>
376     * @param seeEscapedNewLinesAsText <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_SEE_ESC_NL>
377     * @param printNulls <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_PRINT_NULLS>
378     * @param showLineNumbers <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_SHOW_LNUMS>
379     * @return <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_RETURNS>
380     * @see StringParse#zeroPad10e2(int)
381     * @see #abbrevEndRDSF(String, int, boolean)
382     * @see StringParse#trimLeft(String)
383     */
384    @LinkJavaSource(handle="PrintListAbbrev", name="print2")
385    public static <ELEM> String printListAbbrev(
386            ELEM[]                              list,
387            IntTFunction<? super ELEM, String>  listItemPrinter,
388            int                                 lineWidth,
389            int                                 indentation,
390            boolean                             seeEscapedNewLinesAsText,
391            boolean                             printNulls,
392            boolean                             showLineNumbers
393        )
394    {
395        return PrintListAbbrev.print2(
396            list, listItemPrinter, lineWidth, indentation, seeEscapedNewLinesAsText,
397            printNulls, showLineNumbers
398        );
399    }
400
401
402    // ********************************************************************************************
403    // ********************************************************************************************
404    // Line(s) of Text
405    // ********************************************************************************************
406    // ********************************************************************************************
407
408
409    /**
410     * This will return the complete text-lines of character data for the line identified by
411     * token-substring position parameters {@code pos} and {@code len}.  This method will search,
412     * in the left-direction (decreasing {@code String} index) for the first new-line character
413     * identified. It will also search, starting at position {@code pos + len}, for the first
414     * new-line character in the right-direction (increasing {@code String} index).
415     * 
416     * <BR /><BR />If the token-substring identified by {@code s.substring(pos, len)} itself
417     * contains any new-line characters, these will neither affect the prepended, nor the
418     * post-pended search {@code String}.  To be precise, any newline characters between
419     * {@code 'pos'} and {@code 'len'} will be irrelevant to the left-wards and right-wards
420     * newlines searches for new-line characters.
421     * 
422     * @param s This may be any valid Java {@code String}.  It ought to be some variant of a
423     * text-file. It will be searched for the nearest {@code '\n'} character - <I>in both
424     * directions, left and right</I> - from parameter {@code int 'pos'} and parameter
425     * {@code int 'len'}
426     * 
427     * @param pos This is a position in the input-parameter {@code String 's'}.  The nearest
428     * {@code '\n'} (new-line) character, <I>to the left</I> of this position, will be found and
429     * identified.  If the {@code char} at {@code s.charAt(pos)} is, itself, a {@code '\n'}
430     * (newline) character, then no left-direction search will be performed.  The left-most
431     * position of the returned substring would then be {@code pos + 1}.
432     * 
433     * @param len The search for the 'right-most' {@code '\n'} (newline-character) will begin at
434     * position {@code 'len'}.  If the character at {@code s.charAt(pos + len)} is, itself, a 
435     * new-line character, then no right-direction search will be performed.  The right-most
436     * position of the returned substring would be {@code pos + len - 1}.
437     * 
438     * @param unixColorCode If this {@code String} is null, it will be ignored.  If this
439     * {@code String} is non-null, it will be inserted before the "Matching {@code String}"
440     * indicated by the index-boundaries {@code pos} <I>TO</I> {@code pos + len}.
441     * 
442     * <BR /><BR /><B>NOTE:</B> No Validity Check shall be performed on this {@code String}, and
443     * the user is not obligated to provide a {@link C} valid UNIX Color-Code {@code String}.
444     * Also, a closing {@code C.RESET} is inserted after the terminus of the match.
445     * 
446     * @return The {@code String} demarcated by the first new-line character PLUS 1
447     * <I><B>BEFORE</I></B> index {@code 'pos'}, and the first new-line character MINUS 1
448     * <I><B>AFTER</I></B> index {@code pos + len}.
449     * 
450     * <BR /><BR /><B>NOTE:</B> The above does mean, indeed, that the starting and ending
451     * new-lines <B>WILL NOT</B> be included in the returned {@code String}.
452     * 
453     * <BR /><BR /><B>ALSO:</B> Also, if there are no new-line characters before position 
454     * {@code 'pos'}, then every character beginning at position zero will be included in the
455     * returned {@code String} result.  Also, if there are no new-line characters after position
456     * {@code pos + len} then every character after position {@code pos + len} will be appended to
457     * the returned {@code String} result.
458     * 
459     * @throws StringIndexOutOfBoundsException If either {@code 'pos'}, or {@code 'pos + len'} are
460     * not within the bounds of the input {@code String 's'}
461     * 
462     * @throws IllegalArgumentException If the value passed to parameter {@code 'len'} is zero or
463     * negative.
464     */
465    public static String lineOrLines(String s, int pos, int len, String unixColorCode)
466    {
467        if ((pos >= s.length()) || (pos < 0)) throw new StringIndexOutOfBoundsException(
468            "The integer passed to parameter 'pos' [" + pos + "], is past the bounds of the end " +
469            "of String 's', which has length [" + s.length() + "]"
470        );
471
472        if (len <= 0) throw new IllegalArgumentException
473            ("The value passed to parameter 'len' [" + len + "], may not be negative.");
474
475        if ((pos + len) > s.length()) throw new StringIndexOutOfBoundsException(
476            "The total of parameter 'pos' [" + pos + "], and parameter 'len' [" + len + "], is: " +
477            "[" + (pos + len) + "].  Unfortunately, String parameter 's' only has length " +
478            "[" + s.length() + "]"
479        );
480
481        int linesStart, linesEnd, temp;
482
483        if (pos == 0)                                           linesStart  = 0;
484        else if (s.charAt(pos) == '\n')                         linesStart  = pos + 1; 
485        else if ((temp = StrIndexOf.left(s, pos, '\n')) != -1)  linesStart  = temp + 1;
486        else                                                    linesStart  = 0;
487
488        if ((pos + len) == s.length())                          linesEnd    = s.length();
489        else if (s.charAt(pos + len) == '\n')                   linesEnd    = pos + len;
490        else if ((temp = s.indexOf('\n', pos + len)) != -1)     linesEnd    = temp;
491        else                                                    linesEnd    = s.length();
492
493        /*
494        // VERY USEFUL FOR DEBUGGING.  DO NOT DELETE...
495        // NOTE: This method is the one that GREP uses.
496        System.out.println("s.charAt(pos)\t\t= "    + "[" + s.charAt(pos) + "]");
497        System.out.println("s.charAt(pos+len)\t= "  + "[" + s.charAt(pos+len) + "]");
498        System.out.println("s.length()\t\t= "       + s.length());
499        System.out.println("pos\t\t\t= "            + pos);
500        System.out.println("pos + len\t\t= "        + (pos + len));
501        System.out.println("linesStart\t\t= "       + linesStart);
502        System.out.println("linesEnd\t\t= "         + linesEnd);
503        */
504
505        return  (unixColorCode != null)
506            ?   s.substring(linesStart, pos) + 
507                unixColorCode + s.substring(pos, pos + len) + RESET + 
508                s.substring(pos + len, linesEnd)
509            :   s.substring(linesStart, linesEnd);
510
511                /*
512                OOPS.... For Posterity, this shall remain, here, but commented
513                s.substring(linesStart, pos) + 
514                s.substring(pos, pos + len) + 
515                s.substring(pos + len, linesEnd);
516                */
517    }
518
519    /**
520     * This will return the complete text-line of character data from 'inside' the input-parameter
521     * {@code String s}.  The meaning of {@code 'complete text-line'}, in this method, is that any
522     * and all character data between the first {@code '\n'} (new-line character) when scanning
523     * towards <I>the right</I> (increasing {@code String}-index) and the first {@code '\n'}
524     * character when scanning towards <I>the left</I> (decreasing index) constitutes
525     * {@code 'a line'} (of text-data).
526     * 
527     * <BR /><BR />This scan shall for the left-most and right-most new-line shall begin at 
528     * {@code String}-index parameter {@code pos}.  If either the left-direction or
529     * right-direction scan does not find any new-line characters, then start and end indices of
530     * the returned line of text shall be demarcated by input-{@code String} index {@code '0'} and
531     * index {@code String.length()}, <I><B>respectively</B></I>
532     * 
533     * @param s This may be any valid Java {@code String}.  It ought to be some variant of a
534     * text-file. It will be searched for the nearest {@code '\n'} character - <I>in both
535     * directions, left and right</I> - from parameter {@code int 'pos'}.
536     * 
537     * @param pos This is a position in the input-parameter {@code String 's'}.  The nearest
538     * new-line character both to the left of this position, and to the right, will be found and
539     * identified. If the character at {@code s.charAt(pos)} is itself a newline {@code '\n'}
540     * character, then <I>an exception shall throw</I>.
541     * 
542     * @return The {@code String} identified by the first new-line character PLUS 1
543     * <I><B>BEFORE</I></B> index {@code 'pos'}, and the first new-line character MINUS 1
544     * <I><B>AFTER</I></B> index {@code 'pos + len'}.
545     * 
546     * <BR /><BR /><B>NOTE:</B> The above means, that the starting and ending new-lines,
547     * themselves, will not be included in the {@code String} that is returned.
548     * 
549     * <BR /><BR /><B>ALSO:</B> Also, if there are no new-line characters before position 
550     * {@code 'pos'}, then every character beginning at position zero will be included in the
551     * returned {@code String} result.  Also, if there are no new-line characters after position
552     * {@code 'pos + len'} then every character after position {@code 'pos + len'} will be appended
553     * to the returned {@code String} result.
554     * 
555     * @throws StringIndexOutOfBoundsException If {@code 'pos'} is not within the bounds of the 
556     * input {@code String 's'}
557     * 
558     * @throws IllegalArgumentException If the character in {@code String 's'} at position
559     * {@code 'pos'} is a newline {@code '\n'}, itself.
560     */
561    public static String line(String s, int pos)
562    {
563        if ((pos > s.length()) || (pos < 0)) throw new StringIndexOutOfBoundsException(
564            "The integer passed to parameter 'pos' [" + pos + "], is past the bounds of the end of " +
565            "String 's', which has length [" + s.length() + "]"
566        );
567
568        if (s.charAt(pos) == '\n') throw  new IllegalArgumentException(
569            "The position-index for string-parameter 's' contains, itself, a new line character " +
570            "'\\n.'  This is not allowed here."
571        );
572
573        int lineStart, lineEnd;
574
575        // Prevents StrIndexOf from throwing StringINdexOutOfBounds
576        if (pos == 0) lineStart = 0;
577
578        // Also prevent lineStart equal-to '-1'
579        else if ((lineStart = StrIndexOf.left(s, pos, '\n')) == -1) lineStart = 0;
580
581        // Prevent lineEnd equal to '-1'
582        if ((lineEnd = s.indexOf('\n', pos)) == -1) lineEnd = s.length();
583
584        // if this is the first line, there was no initial '\n', so don't skip it!
585        return (lineStart == 0)
586
587            // This version returns the String from the position-0 (Pay Attention!)
588            ? s.substring(0, lineEnd)
589
590            // This version simply eliminates the '\n' that is in the directly-preceeding character
591            : s.substring(lineStart + 1, lineEnd);
592    }
593
594    /**
595     * This will retrieve the first {@code 'n'} lines of a {@code String} - where a line is defined
596     * as everything up to and including the next newline {@code '\n'} character.
597     * 
598     * @param s Any java {@code String}.
599     * 
600     * @param n This is the number of lines of text to retrieve.
601     * 
602     * @return a substring of s where the last character in the {@code String} is a {@code '\n'}.
603     * The last character should be the nth {@code '\n'} character found in s.  If there is no such
604     * character, then the original {@code String} shall be returned instead.
605     * 
606     * @throws NException This exception shall throw if parameter {@code 'n'} is less than 1, or
607     * longer than {@code s.length()}.
608     */
609    public static String firstNLines(String s, int n)
610    {
611        NException.check(n, s);
612        int pos = StrIndexOf.nth(s, n, '\n');
613
614        if (pos != -1)  return s.substring(0, pos + 1);
615        else            return s;
616    }
617
618    /**
619     * This will retrieve the last 'n' lines of a {@code String} - where a line is defined as
620     * everything up to and including the next newline {@code '\n'} character.
621     * 
622     * @param s Any java {@code String}.
623     * 
624     * @param n This is the number of lines of text to retrieve.
625     * 
626     * @return a substring of {@code 's'} where the last character in the {@code String} is a
627     * new-line character {@code '\n'}, and the first character is the character directly before
628     * the nth newline {@code '\n'} found in {@code 's'} - starting the count at the end of the
629     * {@code String}.  If there is no such substring, then the original {@code String} shall be
630     * returned.
631     * 
632     * @throws NException This exception shall throw if {@code 'n'} is less than 1, or longer
633     * {@code s.length()}.
634     */
635    public static String lastNLines(String s, int n)
636    {
637        NException.check(n, s);
638        int pos = StrIndexOf.nthFromEnd(s, n, '\n');
639
640        if (pos != -1)  return s.substring(pos + 1);
641        else            return s;
642    }
643
644    /**
645     * This is used for "trimming each line" of an input {@code String}.  Generally, when dealing
646     * with HTML there may be superfluous white-space that is useful in some places, but not
647     * necessarily when HTML is copied and pasted to other sections of a page (or to another page,
648     * altogether).  This will split a {@code String} by new-line characters, and then trim each
649     * line, and afterward rebuild the {@code String} and return it. 
650     * 
651     * <BR /><BR /><B CLASS=JDDescLabel>CRLF Issues:</B>
652     * 
653     * <BR />This will only split the {@code String} using the standard {@code '\n'} character.  If
654     * the {@code String} being used uses {@code '\r'} or {@code '\n\r'}, use a different trim
655     * method.
656     * 
657     * @param str This may be any {@code String}.  It will be split by new-line characters
658     * {@code '\n'}
659     * 
660     * @return Returns the rebuilt {@code String}, with each line having a {@code String.trim();}
661     * operation performed.
662     */
663    public static String trimEachLine(String str)
664    {
665        StringBuilder sb = new StringBuilder();
666
667        for (String s : str.split("\\n"))
668
669            if ((s = s.trim()).length() == 0)   continue;
670            else                                sb.append(s + '\n');
671
672        return sb.toString().trim();
673    }
674
675    /**
676     * Interprets an input {@code String} as one which was read out of a Text-File.  Counts the
677     * number of new-line ({@code '\n'}) characters between {@code String} indices {@code '0'} and
678     * {@code 'pos'}
679     * 
680     * <BR /><BR />This is intended be the Line-Number where {@code String}-Index parameter
681     * {@code 'pos'} is located inside the {@code 'str'} (presuming {@code 'str'} was retrieved
682     * from a Text-File).
683     * 
684     * @param str Any Java {@code String}, preferably one with multiple lines of text.
685     * @param pos Any valid {@code String}-Index that occurs ithin {@code 'str'}
686     * 
687     * @return The Line-Number within Text-File parameter {@code 'str'} which contains
688     * {@code String}-Index parameter {@code 'pos'}
689     * 
690     * @throws StringIndexOutOfBoundsException If integer-parameter {@code 'pos'} is negative or
691     * past the length of the {@code String}-Parameter {@code 'str'}.
692     * 
693     * @see #lineNumberSince(String, int, int, int)
694     */
695    public static int lineNumber(String str, int pos)
696    {
697        if (pos < 0) throw new StringIndexOutOfBoundsException
698            ("The number provided to index parameter 'pos' : [" + pos + "] is negative.");
699
700        if (pos >= str.length()) throw new StringIndexOutOfBoundsException(
701            "The number provided to index parameter 'pos' : [" + pos + "] is greater than the " +
702            "length of the input String-Parameter 'str' [" + str.length() + "]."
703        );
704
705        int lineNum = 1;
706
707        for (int i=0; i <= pos; i++) if (str.charAt(i) == '\n') lineNum++;
708
709        return lineNum;
710    }
711
712    /**
713     * This methe may be used, iteratively, inside of a loop for finding the location of any
714     * subsequent {@code String}-Index within a Text-File, based on the information obtained from
715     * a previous Line-Number retrieval.
716     * 
717     * <BR /><BR /><B CLASS=JDDescLabel>Use inside For-Loop:</B>
718     * 
719     * <BR />This method is designed to be used within a {@code 'for'} or {@code 'while'} loop.  
720     * Though it is true that the exception-check which occurs inside this method is superfluous
721     * and redundant, the cost incurred by the two {@code if}-statements is minimal.  These checks
722     * are used here, in the code, primarily for readability.
723     * 
724     * <BR /><BR />If maximum efficiency is needed, then copy and paste the bottom two lines of
725     * code into your editor, and use that instead, without the exception-checks.
726     * 
727     * <BR />In the example below, it should be noted how to use both methods to iterate through
728     * the line numbers in a Text-File, efficiently.
729     * 
730     * <DIV CLASS=EXAMPLE>{@code
731     * 
732     * int[]    posArr      = findIndices(myString, "Raindrops on Roses", "Whiskers on Kittens");
733     * int      lineNumber  = StrPrint.lineNumber(myString, posArr[0]);
734     * int      prevPos     = posArr[0];
735     * 
736     * System.out.println("There is a match on line: " + lineNumber);
737     * 
738     * for (int i=1; i < posArr.length; i++)
739     * {
740     *      lineNumber = StrPrint.lineNumberSince(myString, posArr[i], lineNumber, prevPos);
741     *      System.out.println("There is a match on line: " + lineNumber);
742     * 
743     *      prevPos = posArr[i];
744     * }
745     * }</DIV>
746     * 
747     * @param str Any Java {@code String}.  This {@code String} will be interpreted as a Text-File
748     * whose newline characters ({@code '\n'} chars) represent lines of text.
749     * 
750     * @param pos Any valid {@code String}-index within {@code 'str'}
751     * 
752     * @param prevLineNum This should be the Line-Number that contains the {@code String}-index
753     * {@code 'prevPos'}
754     * 
755     * @param prevPos This may be any index contained by {@code String} parameter {@code 'str'}.
756     * It is expected that this parameter be an index that occured on Line-Number
757     * {@code 'prevLineNum'} of the Text-File {@code 'str'}
758     * 
759     * @return The Line-Number within Text-File parameter {@code 'str'} that contains
760     * {@code String}-index {@code 'pos'}
761     * 
762     * @throws IllegalArgumentException If {@code 'pos'} is less than or equal to
763     * {@code 'prevPos'}, or if {@code 'prevLineNum'} is less than zero.
764     * 
765     * @see #lineNumber(String, int)
766     */
767    public static int lineNumberSince(String str, int pos, int prevLineNum, int prevPos)
768    {
769        if (pos <= prevPos) throw new IllegalArgumentException(
770            "The number provided to index parameter 'pos' : [" + pos + "] is less than or equal " +
771            "to previous-match index-parameter prevPos : [" + prevPos + "]"
772        );
773
774        if (prevLineNum < 0) throw new IllegalArgumentException(
775            "You have provided a negative number to Line-Number parameter 'prevLineNum' : " +
776            "[" + prevLineNum + "]"
777        );
778
779        for (int i = (prevPos + 1); i <= pos; i++) if (str.charAt(i) == '\n') prevLineNum++;
780
781        return prevLineNum;
782    }
783
784
785    // ********************************************************************************************
786    // ********************************************************************************************
787    // Abbreviation: Line-Length **AND** Number of Lines
788    // ********************************************************************************************
789    // ********************************************************************************************
790
791
792    /**
793     * This method allows for printing an abbreviation of a {@code String} such that
794     * <B STYLE='color: red;'><I>BOTH</I></B> the number of lines (height),
795     * <B STYLE='color: red;'><I>AND</I></B> the length of each line of text (width) can both be
796     * abbreviated.  There is even a third abbreviation that is possible, and that is where 
797     * blank-links can be compacted / flattened first (before the abbreviation process starts).
798     * 
799     * <BR /><BR />This method is being used for printing JSON-Response Objects that contain large
800     * HTML-Page {@code String's}.  Primarily, if you want to see a message, but do not want
801     * hundreds or even thousands of lines of HTML blasted across your terminal, then this method is
802     * for you!
803     * 
804     * <BR /><BR />The package {@code Torello.Browser's} Web-Sockets Communications is making use
805     * of this for printing Chrome-Browser Messages to the terminal.
806     * 
807     * @param s Any Java {@code String}
808     * 
809     * @param horizAbbrevStr If you have a specific Abbreviation-{@code String} that you would like
810     * to see used in Horizontally-Abbreviated Lines, please pass it here.
811     * 
812     * <BR /><BR />Note that this value is directly passed to the {@code 'abbrevStr'} parameters
813     * in the Standard {@code String}-Abbreviation methods.  This means that when this parameter is
814     * null, it will be ignored - <I>and if any horizontal (line-length) abbreviations occur, then
815     *  the 'Default-Abbreviation {@code String}' {@code "..."} will be used</I>.
816     * 
817     * <BR ><BR />Also note that if parameter {@code 'maxLineLength'} is passed null, then lines of
818     * text will not be shortened.  In that case, each line of text will retain its exact length 
819     * that occured prior to the internal {@code String.split()} invocation.
820     * 
821     * @param maxLineLength If you would like to shorten each line of text which is appended to the
822     * returned {@code String}, then pass a positive value to this parameter.
823     * 
824     * <BR /><BR />This parameter may be null, and if it is, it will be ignored.  In such cases, 
825     * individual lines of text will retain their original length.
826     * 
827     * @param maxNumLines This is the maximum number of lines of text that will appear in the
828     * returned {@code String}.  Note, under normal operation, if this parameter is passed 
829     * {@code '3'}, then there will be exactly three lines of text.  Furthermore, there will be
830     * exactly {@code '2'} newline {@code '\n'} characters.
831     * 
832     * @param compactConsecutiveBlankLines When this parameter is passed {@code TRUE}, any 
833     * series of Empty-Lines, or lines only containing White-Space will be compacted to a single 
834     * line of text that simply states {@code "[Compacted 10 Blank Lines]"} (or however many 
835     * White-Space-Only Lines were actually compacted, if that number isn't {@code '10'})
836     * 
837     * @return The modified {@code String}.
838     * 
839     * @throws IllegalArgumentException If parameter {@code 'maxNumLines'} is less than 3.  The
840     * returned {@code String} must be long enough to keep the first line, the last line and the
841     * abbreviation note.
842     * 
843     * <BR /><BR />This exception will also throw if the internal invocation of the standard 
844     * {@code String}-abbreviation method is passed a {@code 'maxLineLength'} that is less than
845     * the minimum line-length requirements for a successful horizontal abbreviation.
846     * 
847     * <BR /><BR />Finally, this exception throws if {@code 'maxLineLength'} and
848     * {@code 'maxNumLines'} are both passed null, and {@code 'compactConsecutiveBlankLines'} is
849     * {@code FALSE}.  This scenario is considered an error-case because there wouldn't be anything
850     * for the method to do.
851     */
852    @LinkJavaSource(handle="VertAndHorizAbbrev")
853    public static String widthHeightAbbrev(
854            final String    s,
855            final String    horizAbbrevStr,
856            final Integer   maxLineLength,
857            final Integer   maxNumLines,
858            final boolean   compactConsecutiveBlankLines
859        )
860    {
861        return VertAndHorizAbbrev.print
862            (s, horizAbbrevStr, maxLineLength, maxNumLines, compactConsecutiveBlankLines);
863    }
864
865
866    // ********************************************************************************************
867    // ********************************************************************************************
868    // Wrap Text to another line
869    // ********************************************************************************************
870    // ********************************************************************************************
871
872
873    /**
874     * Convenience Method.
875     * <BR />See Documentation: {@link #wrap(String, int, int)}
876     * <BR />Passes same {@code 'lineLen'} value to <B><I>BOTH</I></B> Line-Length Parameters
877     */
878    @LinkJavaSource(handle="Wrap")
879    @LinkJavaSource(handle="WrapHelpers")
880    public static String wrap(String s, int lineLen)
881    { return Wrap.wrap(s, lineLen, lineLen); }
882
883    /**
884     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_DESCRIPTION>
885     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_RIGHT_TRIM>
886     * 
887     * @param s             Any Java {@code String}; may not contain {@code '\t', '\f' or '\r'}
888     * @param firstLineLen  <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_1ST_LN_LEN>
889     * @param lineLen       The maximum allowable Line-Length for all other lines of text.
890     * @return              <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_RETURNS>
891     * 
892     * @throws StringFormatException <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_STR_FMT_EX>
893     * @throws IllegalArgumentException If {@code 'firstLineLen'} or {@code 'lineLen'} are
894     * zero or negative.
895     */
896    @LinkJavaSource(handle="Wrap")
897    @LinkJavaSource(handle="WrapHelpers")
898    public static String wrap(String s, int firstLineLen, int lineLen)
899    { return Wrap.wrap(s, firstLineLen, lineLen); }
900 
901
902    /**
903     * Convenience Method.
904     * <BR />See Documentation: {@link #wrapToIndentation(String, int, int)}
905     * <BR />Passes same {@code 'lineLen'} value to <B><I>BOTH</I></B> Line-Length Parameters
906     */
907    @LinkJavaSource(handle="WrapToIndentation")
908    @LinkJavaSource(handle="WrapHelpers")
909    public static String wrapToIndentation(String s, int lineLen)
910    { return WrapToIndentation.wrap(s, lineLen, lineLen); }
911
912    /**
913     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_DESCRIPTION>
914     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_RIGHT_TRIM>
915     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_TO_INDENT>
916     * 
917     * @param s             Any Java {@code String}; may not contain {@code '\t', '\f' or '\r'}
918     * @param firstLineLen  <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_1ST_LN_LEN>
919     * @param lineLen       The maximum allowable Line-Length for all other lines of text.
920     * @return              <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_RETURNS>
921     * 
922     * <BR /><BR />As already explained, the returned {@code String} shall contain "wrapped" text 
923     * which has an amount of indentation (space characters) equal to whatever line is directly
924     * above it.  Indented lines, effectively, "inherit" the amount of indentation present in the 
925     * line coming directly before them.
926     * 
927     * @throws StringFormatException <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_STR_FMT_EX>
928     * @throws IllegalArgumentException If {@code 'firstLineLen'} or {@code 'lineLen'} are
929     * zero or negative.
930     */
931    @LinkJavaSource(handle="WrapToIndentation")
932    @LinkJavaSource(handle="WrapHelpers")
933    public static String wrapToIndentation(String s, int firstLineLen, int lineLen)
934    { return WrapToIndentation.wrap(s, firstLineLen, lineLen); }
935
936    /**
937     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_DESCRIPTION>
938     * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_RIGHT_TRIM>
939     * 
940     * @param s             Any Java {@code String}; may not contain {@code '\t', '\f' or '\r'}
941     * @param firstLineLen  <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_1ST_LN_LEN>
942     * @param lineLen       The maximum allowable Line-Length for all other lines of text.
943     * 
944     * @param extraSpaces   Wrapped text will get an additional amount of indentation, past 
945     *                      any and all inherited indentation
946     * @return Wrapped.
947     * 
948     * @throws StringFormatException <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_STR_FMT_EX>
949     * @throws IllegalArgumentException If {@code 'firstLineLen'} or {@code 'lineLen'} are
950     * zero or negative.
951     */
952    @LinkJavaSource(handle="WrapToIndentationPlus")
953    @LinkJavaSource(handle="WrapHelpers")
954    public static String wrapToIndentationPlus
955        (String s, int firstLineLen, int lineLen, int extraSpaces)
956    { return WrapToIndentationPlus.wrap(s, firstLineLen, lineLen, extraSpaces); }
957}