001package Torello.Java;
002
003import Torello.Java.Function.*;
004
005import java.util.*;
006import java.util.function.*;
007import java.util.stream.*;
008
009/**
010 * A utility that builds Comma Separated Value String's (and can parse them as well).
011 * 
012 * <BR /><BR /><EMBED CLASS='external-html' DATA-FILE-ID=STR_CSV>
013 */
014@Torello.JavaDoc.StaticFunctional
015public class StrCSV
016{
017    private StrCSV() { }
018
019    private static void throwNPE(int i, Object o)
020    {
021        throw new NullPointerException(
022            "The toString Lambda provided to this method returned null for array-index " +
023            "[" + i + "].  At this index, the array contents were [" + o.toString() + "]"
024        );
025    }
026
027    private static final String IAE_MESSAGE_MAXLENINNER =
028        "The value passed to parameter 'maxLengthInner' was [TOK].  " +
029        "However, BECAUSE this value represents a minimum String length for a subarray, " +
030        "AND BECAUSE an ellipsis, space and brackets are appended, this parameter may not " +
031        "have a positive value less than 7.";
032
033
034    // ********************************************************************************************
035    // ********************************************************************************************
036    // From CSV to Array
037    // ********************************************************************************************
038    // ********************************************************************************************
039
040
041    /**
042     * Convenience Method.
043     * <BR />Invokes: {@link #CSV(String, boolean, boolean)}.
044     */
045    public static String[] CSV(String s) { return CSV(s, true, true); }
046
047    /**
048     * This will return a list of {@code String} that are in-between each-and-every comma that is
049     * found inside the parameter-{@code String} {@code 's'}
050     *
051     * <BR /><BR /><B CLASS=JDDescLabel>Java Stream API:</B>
052     * 
053     * <BR />This method uses Java 8 or 9's {@code package java.util.stream.*}.  If this package is
054     * not familiar, it is usually just a way (after some practice), of (sort-of) converting
055     * for-loops into more readable method-calls.  Java-Streams instead substitute words such as:
056     * {@code 'filter', 'map', 'forEach'} and {@code 'toArray'}. This method's code is nothing more
057     * than that.
058     *
059     * @param s This accepts any java-{@code String}, but it is expecting one that contains commas.
060     * 
061     * @param performTrim If this parameter is set to {@code TRUE}, then all {@code String's} will
062     * be trimmed of white-space before being placed in the returned {@code String}-array, by
063     * calling Java's {@code String.trim()} method.
064     * 
065     * @param eliminateZeroLengthStrings If this parameter is set to {@code TRUE}, then all 
066     * {@code String's} that have zero-length will be eliminated from the returned {@code String[]}
067     * array.
068     * 
069     * <BR /><BR /><B><SPAN STYLE="color: red;">NOTE:</B></SPAN> Regardless of whether or not a
070     * {@code trim()} operation was performed, all {@code String's} that are trimmed of
071     * white-space, would have the {@code 'trim'} done <B><I>before</B></I> the {@code 'eliminate'}
072     * operation.
073     * 
074     * @return This will return the individual {@code String's} from a larger-{@code String} that
075     * contained comma-separated values.
076     */
077    public static String[] CSV(String s, boolean performTrim, boolean eliminateZeroLengthStrings)
078    {
079        Stream<String> stream =
080            StringParse.COMMA_REGEX.splitAsStream(s).filter((String csv) -> csv != null);
081
082        if (performTrim)
083            stream = stream.map((String csv) -> csv.trim());
084
085        if (eliminateZeroLengthStrings)
086            stream = stream.filter((String csv) -> csv.length() > 0);
087
088        return stream.toArray(String[]::new);
089    }
090
091
092    // ********************************************************************************************
093    // ********************************************************************************************
094    // To CSV, Object Arrays, Iterables
095    // ********************************************************************************************
096    // ********************************************************************************************
097
098
099    /**
100     * Convenience Method.
101     * <BR />Invokes: {@link #toCSV(Iterable, boolean, boolean, Integer)}
102     * <BR />Converts: {@code String[] Array} to {@code List<String>}.
103     */
104    public static String toCSV(String[] sArr, boolean trim, boolean printNulls, Integer maxLength)
105    { return toCSV(Arrays.asList(sArr), trim, printNulls, maxLength); }
106
107    /**
108     * This method will turn the elements of any java {@code Iterable} type into a 
109     * {@code java.lang.String}.  The returned {@code String} shall have the individual elements
110     * of parameter {@code 'i'} converted to {@code String's}, each separated by a comma.
111     * 
112     * @param i Any Java {@code Iterable}.  The Java {@code Object.toString()} will be invoked on
113     * each of the elements produced by the {@code Iterable}, and commas shall be inserted between
114     * each element.
115     * 
116     * <EMBED CLASS='external-html' DATA-FILE-ID=RAWTYPES>
117     * 
118     * @param trim This is a boolean, and when set {@code TRUE}, the {@code String.trim()} shall be
119     * invoked on each {@code String} inserted into the CSV list before insertion.
120     * 
121     * @param printNulls This is a boolean, and when set {@code TRUE}, each object returned by the
122     * iterator shall be checked for a 'null' value before insertion into the output-{@code String}
123     * - <I>to avoid null-pointer exceptions</I>.  Instead the four-character {@code String} 'null'
124     * will be inserted instead of throwing this exception.
125     * 
126     * <BR /><BR />When this parameter receives {@code FALSE}, if the input parameter
127     * {@code Iterable<?> i} contains a null value, then this method will simply throw a 
128     * {@code NullPointerException}.
129     * 
130     * @param maxLength <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAX_LEN>
131     * 
132     * @return This will return a CSV {@code String} containing the individual elements of the
133     * input {@code Iterable} parameter {@code 'i'}, where each element has been converted to a
134     * {@code String} and is separated by a comma.
135     * 
136     * @throws NullPointerException If the {@code Iterable} returns a null value, and the
137     * {@code 'printNulls'} parameter were set to {@code FALSE}, then this method would throw
138     * an exception.
139     */
140    public static String toCSV(Iterable<?> i, boolean trim, boolean printNulls, Integer maxLength)
141    {
142        StringBuilder   sb      = new StringBuilder();
143        boolean         first   = true;
144
145        if (trim && printNulls)
146
147            for (Object o : i)
148            {
149                if (o == null) o = "null";
150
151                sb.append((first ? "" : ", ") + o.toString().trim());
152                first = false;
153            }
154
155        else if (trim && (! printNulls))
156
157            for (Object o : i)
158            {
159                sb.append((first ? "" : ", ") + o.toString().trim());
160                first = false;
161            }
162
163        else if ((! trim) && printNulls)
164
165            for (Object o : i)
166            {
167                if (o == null) o = "null";
168    
169                sb.append((first ? "" : ", ") + o.toString());
170                first = false;
171            }
172
173        else // !trim !printNulls
174
175            for (Object o : i)
176            {
177                sb.append((first ? "" : ", ") + o.toString());
178                first = false;
179            }
180
181        if ((maxLength != null) && (sb.length() > maxLength.intValue()))
182        {
183            sb.setLength(maxLength - 4);
184            sb.append(" ...");
185
186            return sb.toString();
187        }
188
189        return sb.toString();
190    }
191
192    /**
193     * This method will turn the elements of any java {@code Iterable} type into a 
194     * {@code java.lang.String}.  The returned {@code String} shall have the individual elements
195     * of parameter {@code 'i'} converted to {@code String's}, each separated by a comma.
196     * 
197     * @param <T> The type used by the {@code java.lang.Iterable}.  The {@code 'toString'} 
198     * parameter / function-pointer also must accept this type, or a super-type.
199     * 
200     * @param i Any Java {@code Iterable}.  The functional-interface parameter {@code 'toString'}
201     * will be invoked on each of the elements produced by the {@code Iterable}, and commas shall
202     * be inserted between each element.
203     * 
204     * @param toString This instance of {@code java.util.function.Function<A, B>} must have a
205     * method that accepts a parameter having type {@code 'T'}, and returns a {@code String}.  This
206     * is simply an "over-riding" of Java's basic {@code 'toString()'} method.  In fact, if the
207     * class that is being used for variable-type parameter {@code 'T'} has a {@code 'toString'}
208     * method that is sufficient or "good enough", then this method should not be used, but
209     * rather the simpler method: {@link #toCSV(Iterable, boolean, boolean, Integer)}.
210     * 
211     * @param printNulls When this parameter is {@code TRUE}, the {@code 'toString.apply(T)'}
212     * method shall receive a null value, and the {@code String} results returned by this method
213     * shall be inserted into the output {@code String} when the input {@code Iterable 'i'}
214     * contains a null value.  Thusly, the behavior of this method for 'nulls' in the input
215     * {@code 'i'} parameter should be <I>no different than any other value found within the input
216     * {@code Iterable 'i'}.</I>
217     * 
218     * <BR /><BR />When this parameter receives {@code FALSE}, any time a null value is encountered
219     * from the input {@code Iterable<T> 'i'}, that value shall be skipped and the output
220     * {@code String} that is returned will have one fewer element in it's CSV list.
221     * 
222     * @param maxLength <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAX_LEN>
223     * 
224     * @return This will return a CSV {@code String} containing the individual elements of the
225     * input {@code Iterable} parameter {@code 'i'}, where each element has been converted to a
226     * {@code String} - <I>using the provided {@code 'toString'}</I> function - and is separated by
227     * a comma.
228     */
229    public static <T> String toCSV
230        (Iterable<T> i, Function<? super T, String> toString, boolean printNulls, Integer maxLength)
231    {
232        StringBuilder   sb      = new StringBuilder();
233        boolean         first   = true;
234
235        if (printNulls)
236
237            for (T t : i)
238            {
239                sb.append((first ? "" : ", ") + toString.apply(t));
240                first = false;
241            }
242
243        else
244
245            for (T t : i)
246                if (t != null)
247                {
248                    sb.append((first ? "" : ", ") + toString.apply(t));
249                    first = false;
250                }
251
252        if ((maxLength != null) && (sb.length() > maxLength.intValue()))
253        {
254            sb.setLength(maxLength - 4);
255            sb.append(" ...");
256
257            return sb.toString();
258        }
259
260        return sb.toString();
261    }
262
263    /**
264     * Convenience Method.
265     * <BR />Invokes: {@link #toCSV(Object[], int, int, boolean, boolean, Integer)}
266     */
267    public static <T> String toCSV
268        (T[] tArr, boolean trim, boolean printNulls, Integer maxLength)
269    { return toCSV(tArr, 0, -1, trim, printNulls, maxLength); }
270
271    /**
272     * This method will turn the elements of any java {@code Iterable} type into a 
273     * {@code java.lang.String}.  The returned {@code String} shall have the individual elements
274     * of parameter {@code 'i'} converted to {@code String's}, each separated by a comma.
275     * 
276     * @param tArr Any array type {@code 'T'}.  Java's {@code 'toString'} method will be invoked
277     * on each of these elements, and returned in a {@code String} where each element has been
278     * separated by a comma.
279     * 
280     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSARRAY>
281     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSARRAY>
282     * 
283     * @param trim This is a boolean, and when set {@code TRUE}, the {@code String.trim()} shall be
284     * invoked on each {@code String} inserted into the CSV list before insertion.
285     * 
286     * @param printNulls This is a boolean, and when set {@code TRUE}, each instance of {@code 'T'}
287     * contained by {@code 'tArr'} shall be checked for a 'null' value before insertion into the
288     * output-{@code String} - <I>to avoid null-pointer exceptions</I>.  If a null is found, the
289     * four-character {@code String} 'null' will be inserted instead of throwing this exception.
290     * 
291     * <BR /><BR />When this parameter receives {@code FALSE}, if a null value is encountered within
292     * the input-parameter array {@code T[] tArr}, then this method will, in fact, throw a 
293     * NullPointerException.
294     * 
295     * @param maxLength <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAX_LEN>
296     * 
297     * @return This will return a CSV {@code String} containing the individual elements of the
298     * input array {@code T[] tArr} parameter, where each element shall have been converted to a
299     * {@code String} and separated by a comma.
300     * 
301     * @throws NullPointerException If the {@code Iterable} returns a null value, and the
302     * {@code 'printNulls'} parameter were set to {@code FALSE}, then this method would throw
303     * an exception.
304     * 
305     * @throws ArrayIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=AIOOB_EX>
306     */
307    public static <T> String toCSV
308        (T[] tArr, int sPos, int ePos, boolean trim, boolean printNulls, Integer maxLength)
309    {
310        LV              l       = new LV(tArr, sPos, ePos);
311        StringBuilder   sb      = new StringBuilder();
312        boolean         first   = true;
313
314        if (trim && printNulls)
315
316            for (int i=l.start; i < l.end; i++)
317            {
318                if (tArr[i] == null)
319                    sb.append((first ? "" : ", ") + "null");
320                else
321                    sb.append((first ? "" : ", ") + tArr[i].toString().trim());
322                first = false;
323            }
324
325        else if (trim && (! printNulls))
326
327            for (int i=l.start; i < l.end; i++)
328            {
329                sb.append((first ? "" : ", ") + tArr[i].toString().trim());
330                first = false;
331            }
332
333        else if ((! trim) && printNulls)
334
335            for (int i=l.start; i < l.end; i++)
336            {
337                if (tArr[i] == null)
338                    sb.append((first ? "" : ", ") + "null");
339                else
340                    sb.append((first ? "" : ", ") + tArr[i].toString());
341                first = false;
342            }
343
344        else // !trim !printNulls
345
346            for (int i=l.start; i < l.end; i++)
347            {
348                sb.append((first ? "" : ", ") + tArr[i].toString());
349                first = false;
350            }
351
352        if ((maxLength != null) && (sb.length() > maxLength.intValue()))
353        {
354            sb.setLength(maxLength - 4);
355            sb.append(" ...");
356
357            return sb.toString();
358        }
359
360        return sb.toString();
361    }
362
363    /**
364     * Convenience Method.
365     * <BR />Invokes: {@link #toCSV(Object[], int, int, IntTFunction, boolean, Integer)}
366     */
367    public static <T> String toCSV
368        (T[] tArr, IntTFunction<? super T, String> toString, boolean printNulls, Integer maxLength)
369    { return toCSV(tArr, 0, -1, toString, printNulls, maxLength); }
370
371    /**
372     * Converts an array of a variable-type parameter {@code T[]} to a CSV {@code String}
373     * 
374     * <BR /><BR />This version of {@code 'toCSV'} allows a programmer to define the exact
375     * {@code 'toString()'} that is used on each object-instance in the provided array.  There
376     * is a simpler version of this method where the invokation {@code T.toString()} is 
377     * used instead.
378     * 
379     * @param <T> The type used by the {@code T[]}-Array.  The {@code 'toString'}
380     * parameter / function-pointer also must accept this type.
381     * 
382     * @param tArr An array of {@code Object's} of a given variable-type {@code 'T'}
383     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSARRAY>
384     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSARRAY>
385     * 
386     * @param toString A method that will convert {@code Object's} of type {@code 'T'} to a
387     * {@code String}.  This parameter <I><B>may not</I></B> be null, because in such cases it
388     * would be appropriate to use: {@link #toCSV(Object[], int, int, boolean, boolean, Integer)}
389     * 
390     * <BR /><BR /><B STYLE="color: red;">NOTE:</B> This functional-interface is used
391     * instead of simply calling {@code Object.toString()} in order to allow a programmer to
392     * provide arbitrarily defined {@code toString()} methods.  If the standard {@code toString()}
393     * method for a given {@code Object} is not sufficient, then provide a different one here using
394     * this parameter.
395     * 
396     * <BR /><BR />This {@code 'toString'} functional-interface is expected to accept both an
397     * instance of a type {@code 'T'} variable, <B><I>AND</I></B> an integer.  The integer that is
398     * accepted is simply the array-index where the {@code 'T'} variable parameter is located
399     * within the array.
400     * 
401     * <BR /><BR /><B STYLE="color: red;">NOTE:</B> This function may return a zero-length
402     * {@code String}, and when/if it does, the {@code Object} located at that array-index shall
403     * not be printed to the output-{@code String}.  Also, if this function ever returns null, then
404     * a {@code NullPointerException} shall throw immediately.
405     * 
406     * @param printNulls When this parameter is {@code TRUE}, if a null is encountered within the
407     * input-parameter array {@code T[] tArr}, then null shall be passed to the Functional
408     * Interface input-parameter method {@code toString.apply(T)}, and the {@code String} result
409     * from that method shall be inserted into the {@code String} that is returned.
410     * 
411     * <BR /><BR />When this parameter receives {@code FALSE}, then anytime a null value is found
412     * within the input-parameter array {@code T[] tArr}, it will be skipped completely, and the
413     * output {@code String} will simply contain one fewer output-{@code String}.
414     * 
415     * @param maxLength <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAX_LEN>
416     * 
417     * @return a CSV version of the input parameter {@code 'tArr'} where each instance of
418     * {@code 'T'} shall have been converted to a {@code String} using the provided
419     * {@code 'toString(...)'} method.
420     * 
421     * @throws ArrayIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=AIOOB_EX>
422     * @throws NullPointerException if parameter {@code 'toString'} is null, or if any of the
423     * return values produced by {@code 'toString'} are null
424     */
425    public static <T> String toCSV(
426            T[] tArr, int sPos, int ePos, IntTFunction<? super T, String> toString,
427            boolean printNulls, Integer maxLength
428        )
429    {
430        LV              l   = new LV(tArr, sPos, ePos);
431        StringBuilder   sb  = new StringBuilder();
432        int             i   = l.start;
433        String          s;
434
435        if (printNulls)
436        {
437            for (; i < l.end; i++)
438                if ((s = toString.apply(i, tArr[i])).length() > 0)
439                    { sb.append(s); break; }
440
441            for (i++; i < l.end; i++)
442                if ((s = toString.apply(i, tArr[i])) == null)   throwNPE(i, tArr[i]);
443                else                                            sb.append(", " + s);
444        }
445
446        else
447        {
448            for (; i < l.end; i++)
449                if ((tArr[i] != null) && ((s = toString.apply(i, tArr[i])).length() > 0))
450                    { sb.append(s); break; }
451
452            for (i++; i < l.end; i++)
453                if (tArr[i] != null)
454                    if ((s = toString.apply(i, tArr[i])) == null)   throwNPE(i, tArr[i]);
455                    else                                            sb.append(", " + s);
456        }
457
458        if ((maxLength != null) && (sb.length() > maxLength.intValue()))
459        {
460            sb.setLength(maxLength - 4);
461            sb.append(" ...");
462
463            return sb.toString();
464        }      
465
466        return sb.toString();
467    }
468
469    /**
470     * <EMBED CLASS=defs DATA-Type="<T>">
471     * This class prints a two dimensional {@code <T>}-array to a {@code String}.
472     * 
473     * @param <T> The type used by the {@code T[][]}-Array.  The {@code 'toString'}
474     * parameter / function-pointer also must accept this type.
475     * 
476     * @param tArr           <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_ARR_2D>
477     * @param toString       <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_TSTR_2DARR>
478     * @param keepRow        <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_SUB_PRED>
479     * @param separateLines  <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_SEPL_LINE>
480     * @param maxLengthInner <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAX_LEN_IN>
481     * @param maxLengthOuter <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAXLEN_OUT>
482     * @return               A printed version of this two-dimensional array.
483     * 
484     * @throws NullPointerException If the {@code 'toString'} method is non-null, but returns a
485     * null value.
486     * 
487     * @throws IllegalArgumentException If {@code 'maxLengthInner'} is less than {@code '7'}.
488     */
489    public static <T> String toCSV(
490            T[][] tArr, IntIntTFunc<? super T, String> toString, IntPredicate keepRow,
491            boolean separateLines, Integer maxLengthInner, Integer maxLengthOuter
492        )
493    {
494        // maxLengthInner *MUST* be >= 7, since minimum-string-length is "  [...]"
495        // A value of 0 to 6 must throw an exception.
496        // A negative value is treated the same as 'null'
497
498        if ((maxLengthInner != null) && (maxLengthInner < 7) && (maxLengthInner >= 0))
499
500            throw new IllegalArgumentException
501                (IAE_MESSAGE_MAXLENINNER.replace("TOK", maxLengthInner.toString()));
502
503        else if ((maxLengthInner != null) && (maxLengthInner < 0)) maxLengthInner = null;
504
505        StringBuilder   sbOuter = new StringBuilder();  // Primary StringBuilder
506        StringBuilder   sbInner = new StringBuilder();  // StrinBuilder for the sub/inner arrays
507        String          s       = null;                 // temp variable
508        int             i       = 0;                    // outer-loop loop-counter
509        int             numRows = 0;                    // Count sub-arrays
510
511        // If the value passed to 'maxLengthOuter' is negative, treat it the same as 'null'
512        // SPECIFICALLY: No Max Length.
513
514        if ((maxLengthOuter != null) && (maxLengthOuter < 0)) maxLengthOuter = null;
515
516        sbOuter.append('[');
517
518        for (i=0; i < tArr.length; i++)
519        {
520            // The "Keep Row" Predicate is used to check whether a sub-array should even be
521            // included at all.  If "Keep Row" exists, and it rejects the outer array index,
522            // then the entire row itself should be skipped.
523
524            if ((keepRow != null) && (! keepRow.test(i))) continue;
525
526            numRows++;
527            if ((maxLengthOuter != null) && (numRows > maxLengthOuter)) break;
528
529            int j = 0;
530
531            // System.out.println("end: " + end + ",\ti: " + i + ",\tj: " + j);
532            // The purpose to this sub-loop is such that there is a "provided user option" where
533            // the 'toString-lambda' may return a ZERO-LENGTH-STRING, and when this happens, the
534            // array-location that resulted in a ZERO-LENGTH-STRING shall be ignored/skipped
535            // completely.
536
537            if (toString != null)
538
539                while (     (j < tArr[i].length)
540                        &&  ((s = toString.apply(i, j, tArr[i][j])).length() == 0)
541                )
542                    j++;
543
544            else s = "" + tArr[i][j].toString();
545
546            // If "separateLines" be sure to add a newline.
547            if (separateLines) sbOuter.append('\n');
548
549            // Add the opening brackets to this new sub-array that was just computed.
550            sbInner.append(" [" + s);
551            j++;
552
553            // Main Printing Loop
554            while (j < tArr[i].length)
555            {
556                if (toString != null)
557                {
558                    if ((s = toString.apply(i, j, tArr[i][j++])).length() > 0)
559                        sbInner.append(", " + s);
560                }
561
562                else sbInner.append(", " + tArr[i][j++].toString());
563
564                if ((maxLengthInner != null) && (sbInner.length() > maxLengthInner)) break;
565            }
566
567            // NOTE: The '+ 1' is needed because, as of yet, the trailing ']' has not been added.
568            if ((maxLengthInner != null) && ((sbInner.length() + 1) > maxLengthInner))
569            {
570                int     pos = sbInner.length() - 4;
571                char    c   = sbInner.charAt(pos);
572
573                while ((c != ',') && (c != '[')) c = sbInner.charAt(--pos);
574
575                sbInner.setLength(pos+1);
576                sbInner.append((c == '[') ? "..." : " ...");
577            }
578
579            sbInner.append(']');
580            sbOuter.append(sbInner.toString());
581            sbInner.setLength(0);
582        }
583
584        // This shall only execute if the 
585        if (i < tArr.length) sbOuter.append((separateLines ? "\n" : "") + "  ...");
586
587        sbOuter.append(separateLines ? "\n]" : " ]");
588    
589        return sbOuter.toString();
590    }
591
592    // ********************************************************************************************
593    // ********************************************************************************************
594    // One Dimensional Primitive Arrays
595    // ********************************************************************************************
596    // ********************************************************************************************
597
598
599    /**
600     * Convenience Method.
601     * <BR />Invokes: {@link #toCSV(byte[], int, int, IntByteFunction, Integer)}
602     */
603    public static String toCSV
604        (byte[] arr, IntByteFunction<String> toString, Integer maxLength)
605    { return toCSV(arr, 0, -1, toString, maxLength); }
606
607    /**
608     * <EMBED CLASS=defs DATA-ArrType=byte DATA-Lambda=IntByteFunction DATA-VarName=b>
609     * <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAIN_DESC>
610     * 
611     * @param arr       Any array of {@code byte}.
612     * @param sPos      <EMBED CLASS='external-html' DATA-FILE-ID=SPOSARRAY>
613     * @param ePos      <EMBED CLASS='external-html' DATA-FILE-ID=EPOSARRAY>
614     * @param toString  <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_TO_STRING DATA-word=numbers>
615     * @param maxLength <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAX_LEN>
616     * @return          <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_RETURN>
617     * 
618     * @throws ArrayIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=AIOOB_EX>
619     * @throws NullPointerException           If {@code 'toString'} returns a null value.
620     */
621    public static String toCSV
622        (byte[] arr, int sPos, int ePos, IntByteFunction<String> toString, Integer maxLength)
623    {
624        LV              l   = new LV(sPos, ePos, arr);
625        StringBuilder   sb  = new StringBuilder();
626        int             i   = l.start;
627        String          s;
628
629        if (toString == null)
630        {
631            sb.append(arr[i++]);
632            while (i < l.end) sb.append(", " + arr[i++]);
633        }
634
635        else
636        {
637            for (; i < l.end; i++)
638                if ((s = toString.apply(i, arr[i])).length() > 0)
639                    { sb.append(s); break; }
640
641            for (i++; i < l.end; i++)
642                if ((s = toString.apply(i, arr[i])) == null)    throwNPE(i, arr[i]);
643                else                                            sb.append(", " + s);
644        }
645
646        if ((maxLength != null) && (sb.length() > maxLength.intValue()))
647        {
648            sb.setLength(maxLength - 4);
649            sb.append(" ...");
650
651            return sb.toString();
652        }
653    
654        return sb.toString();
655    }
656
657    /**
658     * Convenience Method.
659     * <BR />Invokes: {@link #toCSV(short[], int, int, IntShortFunction, Integer)}
660     */
661    public static String toCSV
662        (short[] arr, IntShortFunction<String> toString, Integer maxLength)
663    { return toCSV(arr, 0, -1, toString, maxLength); }
664
665    /**
666     * <EMBED CLASS=defs DATA-ArrType=short DATA-Lambda=IntShortFunction DATA-VarName=s>
667     * <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAIN_DESC>
668     * 
669     * @param arr       Any array of {@code short}-integers.
670     * @param sPos      <EMBED CLASS='external-html' DATA-FILE-ID=SPOSARRAY>
671     * @param ePos      <EMBED CLASS='external-html' DATA-FILE-ID=EPOSARRAY>
672     * @param toString  <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_TO_STRING DATA-word=numbers>
673     * @param maxLength <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAX_LEN>
674     * @return          <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_RETURN>
675     * 
676     * @throws ArrayIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=AIOOB_EX>
677     * @throws NullPointerException           If {@code 'toString'} returns a null value.
678     */
679    public static String toCSV
680        (short[] arr, int sPos, int ePos, IntShortFunction<String> toString, Integer maxLength)
681    {
682        LV              l   = new LV(sPos, ePos, arr);
683        StringBuilder   sb  = new StringBuilder();
684        int             i   = l.start;
685        String          s;
686
687        if (toString == null)
688        {
689            sb.append(arr[i++]);
690            while (i < l.end) sb.append(", " + arr[i++]);
691        }
692
693        else
694        {
695            for (; i < l.end; i++)
696                if ((s = toString.apply(i, arr[i])).length() > 0)
697                    { sb.append(s); break; }
698
699            for (i++; i < l.end; i++)
700                if ((s = toString.apply(i, arr[i])) == null)    throwNPE(i, arr[i]);
701                else                                            sb.append(", " + s);
702        }
703
704        if ((maxLength != null) && (sb.length() > maxLength.intValue()))
705        {
706            sb.setLength(maxLength - 4);
707            sb.append(" ...");
708
709            return sb.toString();
710        }
711
712        return sb.toString();
713    }
714
715    /**
716     * Convenience Method.
717     * <BR />Invokes: {@link #toCSV(int[], int, int, BiIntFunction, Integer)}
718     */
719    public static String toCSV
720        (int[] arr, BiIntFunction<String> toString, Integer maxLength)
721    { return toCSV(arr, 0, -1, toString, maxLength); }
722
723    /**
724     * <EMBED CLASS=defs DATA-ArrType=int DATA-Lambda=BiIntFunction DATA-VarName=j>
725     * <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAIN_DESC>
726     * 
727     * @param arr       Any array of integers.
728     * @param sPos      <EMBED CLASS='external-html' DATA-FILE-ID=SPOSARRAY>
729     * @param ePos      <EMBED CLASS='external-html' DATA-FILE-ID=EPOSARRAY>
730     * @param toString  <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_TO_STRING DATA-word=numbers>
731     * @param maxLength <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAX_LEN>
732     * @return          <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_RETURN>
733     * 
734     * @throws ArrayIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=AIOOB_EX>
735     * @throws NullPointerException           If {@code 'toString'} returns a null value.
736     */
737    public static String toCSV
738        (int[] arr, int sPos, int ePos, BiIntFunction<String> toString, Integer maxLength)
739    {
740        LV              l   = new LV(sPos, ePos, arr);
741        StringBuilder   sb  = new StringBuilder();
742        int             i   = l.start;
743        String          s;
744
745        if (toString == null)
746        {
747            sb.append(arr[i++]);
748            while (i < l.end) sb.append(", " + arr[i++]);
749        }
750
751        else
752        {
753            for (; i < l.end; i++)
754                if ((s = toString.apply(i, arr[i])).length() > 0)
755                    { sb.append(s); break; }
756
757            for (i++; i < l.end; i++)
758                if ((s = toString.apply(i, arr[i])) == null)    throwNPE(i, arr[i]);
759                else                                            sb.append(", " + s);
760        }
761
762        if ((maxLength != null) && (sb.length() > maxLength.intValue()))
763        {
764            sb.setLength(maxLength - 4);
765            sb.append(" ...");
766
767            return sb.toString();
768        }
769
770        return sb.toString();
771    }
772
773    /**
774     * Convenience Method.
775     * <BR />Invokes: {@link #toCSV(long[], int, int, IntLongFunction, Integer)}
776     */
777    public static String toCSV
778        (long[] arr, IntLongFunction<String> toString, Integer maxLength)
779    { return toCSV(arr, 0, -1, toString, maxLength); }
780
781    /**
782     * <EMBED CLASS=defs DATA-ArrType=long DATA-Lambda=IntLongFunction DATA-VarName=l>
783     * <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAIN_DESC>
784     * 
785     * @param arr       Any array of {@code long}-integers.
786     * @param sPos      <EMBED CLASS='external-html' DATA-FILE-ID=SPOSARRAY>
787     * @param ePos      <EMBED CLASS='external-html' DATA-FILE-ID=EPOSARRAY>
788     * @param toString  <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_TO_STRING DATA-word=numbers>
789     * @param maxLength <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAX_LEN>
790     * @return          <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_RETURN>
791     * 
792     * @throws ArrayIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=AIOOB_EX>
793     * @throws NullPointerException           If {@code 'toString'} returns a null value.
794     */
795    public static String toCSV
796        (long[] arr, int sPos, int ePos, IntLongFunction<String> toString, Integer maxLength)
797    {
798        LV              l   = new LV(sPos, ePos, arr);
799        StringBuilder   sb  = new StringBuilder();
800        int             i   = l.start;
801        String          s;
802
803        if (toString == null)
804        {
805            sb.append(arr[i++]);
806            while (i < l.end) sb.append(", " + arr[i++]);
807        }
808
809        else
810        {
811            for (; i < l.end; i++)
812                if ((s = toString.apply(i, arr[i])).length() > 0)
813                    { sb.append(s); break; }
814
815            for (i++; i < l.end; i++)
816                if ((s = toString.apply(i, arr[i])) == null)    throwNPE(i, arr[i]);
817                else                                            sb.append(", " + s);
818        }
819
820        if ((maxLength != null) && (sb.length() > maxLength.intValue()))
821        {
822            sb.setLength(maxLength - 4);
823            sb.append(" ...");
824
825            return sb.toString();
826        }
827
828        return sb.toString();
829    }
830
831    /**
832     * Convenience Method.
833     * <BR />Invokes: {@link #toCSV(float[], int, int, IntFloatFunction, Integer)}
834     */
835    public static String toCSV
836        (float[] arr, IntFloatFunction<String> toString, Integer maxLength)
837    { return toCSV(arr, 0, -1, toString, maxLength); }
838
839    /**
840     * <EMBED CLASS=defs DATA-ArrType=float DATA-Lambda=IntFloatFunction DATA-VarName=f>
841     * <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAIN_DESC>
842     * 
843     * @param arr       Any array of {@code float}.
844     * @param sPos      <EMBED CLASS='external-html' DATA-FILE-ID=SPOSARRAY>
845     * @param ePos      <EMBED CLASS='external-html' DATA-FILE-ID=EPOSARRAY>
846     * @param toString  <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_TO_STRING DATA-word=numbers>
847     * @param maxLength <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAX_LEN>
848     * @return          <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_RETURN>
849     * 
850     * @throws ArrayIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=AIOOB_EX>
851     * @throws NullPointerException           If {@code 'toString'} returns a null value.
852     */
853    public static String toCSV
854        (float[] arr, int sPos, int ePos, IntFloatFunction<String> toString, Integer maxLength)
855    {
856        LV              l   = new LV(sPos, ePos, arr);
857        StringBuilder   sb  = new StringBuilder();
858        int             i   = l.start;
859        String          s;
860
861        if (toString == null)
862        {
863            sb.append(arr[i++]);
864            while (i < l.end) sb.append(", " + arr[i++]);
865        }
866
867        else
868        {
869            for (; i < l.end; i++)
870                if ((s = toString.apply(i, arr[i])).length() > 0)
871                    { sb.append(s); break; }
872
873            for (i++; i < l.end; i++)
874                if ((s = toString.apply(i, arr[i])) == null)    throwNPE(i, arr[i]);
875                else                                            sb.append(", " + s);
876        }
877
878        if ((maxLength != null) && (sb.length() > maxLength.intValue()))
879        {
880            sb.setLength(maxLength - 4);
881            sb.append(" ...");
882
883            return sb.toString();
884        }
885    
886        return sb.toString();
887    }
888
889    /**
890     * Convenience Method.
891     * <BR />Invokes: {@link #toCSV(double[], int, int, IntDoubleFunction, Integer)}
892     */
893    public static String toCSV
894        (double[] arr, IntDoubleFunction<String> toString, Integer maxLength)
895    { return toCSV(arr, 0, -1, toString, maxLength); }
896
897    /**
898     * <EMBED CLASS=defs DATA-ArrType=double DATA-Lambda=IntDoubleFunction DATA-VarName=d>
899     * <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAIN_DESC>
900     * 
901     * @param arr       Any array of {@code double}.
902     * @param sPos      <EMBED CLASS='external-html' DATA-FILE-ID=SPOSARRAY>
903     * @param ePos      <EMBED CLASS='external-html' DATA-FILE-ID=EPOSARRAY>
904     * @param toString  <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_TO_STRING DATA-word=numbers>
905     * @param maxLength <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAX_LEN>
906     * @return          <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_RETURN>
907     * 
908     * @throws ArrayIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=AIOOB_EX>
909     * @throws NullPointerException           If {@code 'toString'} returns a null value.
910     */
911    public static String toCSV
912        (double[] arr, int sPos, int ePos, IntDoubleFunction<String> toString, Integer maxLength)
913    {
914        LV              l   = new LV(sPos, ePos, arr);
915        StringBuilder   sb  = new StringBuilder();
916        int             i   = l.start;
917        String          s;
918
919        if (toString == null)
920        {
921            sb.append(arr[i++]);
922            while (i < l.end) sb.append(", " + arr[i++]);
923        }
924
925        else
926        {
927            for (; i < l.end; i++)
928                if ((s = toString.apply(i, arr[i])).length() > 0)
929                    { sb.append(s); break; }
930
931            for (i++; i < l.end; i++)
932                if ((s = toString.apply(i, arr[i])) == null)    throwNPE(i, arr[i]);
933                else                                            sb.append(", " + s);
934        }
935
936        if ((maxLength != null) && (sb.length() > maxLength.intValue()))
937        {
938            sb.setLength(maxLength - 4);
939            sb.append(" ...");
940
941            return sb.toString();
942        }
943    
944        return sb.toString();
945    }
946
947    /**
948     * Convenience Method.
949     * <BR />Invokes: {@link #toCSV(boolean[], int, int, IntBoolFunction, Integer)}
950     */
951    public static String toCSV
952        (boolean[] arr, IntBoolFunction<String> toString, Integer maxLength)
953    { return toCSV(arr, 0, -1, toString, maxLength); }
954
955    /**
956     * <EMBED CLASS=defs DATA-ArrType=boolean DATA-Lambda=IntBoolFunction DATA-VarName=b>
957     * <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAIN_DESC>
958     * 
959     * @param arr       Any array of {@code boolean}.
960     * @param sPos      <EMBED CLASS='external-html' DATA-FILE-ID=SPOSARRAY>
961     * @param ePos      <EMBED CLASS='external-html' DATA-FILE-ID=EPOSARRAY>
962     * @param toString  <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_TO_STRING DATA-word=booleans>
963     * @param maxLength <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAX_LEN>
964     * @return          <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_RETURN>
965     * 
966     * @throws ArrayIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=AIOOB_EX>
967     * @throws NullPointerException           If {@code 'toString'} returns a null value.
968     */
969    public static String toCSV
970        (boolean[] arr, int sPos, int ePos, IntBoolFunction<String> toString, Integer maxLength)
971    {
972        LV              l   = new LV(sPos, ePos, arr);
973        StringBuilder   sb  = new StringBuilder();
974        int             i   = l.start;
975        String          s;
976
977        if (toString == null)
978        {
979            sb.append(arr[i++]);
980            while (i < l.end) sb.append(", " + arr[i++]);
981        }
982
983        else
984        {
985            for (; i < l.end; i++)
986                if ((s = toString.apply(i, arr[i])).length() > 0)
987                    { sb.append(s); break; }
988
989            for (i++; i < l.end; i++)
990                if ((s = toString.apply(i, arr[i])) == null)    throwNPE(i, arr[i]);
991                else                                            sb.append(", " + s);
992        }
993
994        if ((maxLength != null) && (sb.length() > maxLength.intValue()))
995        {
996            sb.setLength(maxLength - 4);
997            sb.append(" ...");
998
999            return sb.toString();
1000        }
1001    
1002        return sb.toString();
1003    }
1004
1005    /**
1006     * Convenience Method.
1007     * <BR />Invokes: {@link #toCSV(char[], int, int, IntCharFunction, Integer)}
1008     */
1009    public static String toCSV
1010        (char[] arr, IntCharFunction<String> toString, Integer maxLength)
1011    { return toCSV(arr, 0, -1, toString, maxLength); }
1012
1013    /**
1014     * <EMBED CLASS=defs DATA-ArrType=char DATA-Lambda=IntCharFunction DATA-VarName=c>
1015     * <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAIN_DESC>
1016     * 
1017     * @param arr       Any array of {@code boolean}.
1018     * @param sPos      <EMBED CLASS='external-html' DATA-FILE-ID=SPOSARRAY>
1019     * @param ePos      <EMBED CLASS='external-html' DATA-FILE-ID=EPOSARRAY>
1020     * @param toString  <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_TO_STRING DATA-word=characters>
1021     * @param maxLength <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAX_LEN>
1022     * @return          <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_RETURN>
1023     * 
1024     * @throws ArrayIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=AIOOB_EX>
1025     * @throws NullPointerException           If {@code 'toString'} returns a null value.
1026     */
1027    public static String toCSV
1028        (char[] arr, int sPos, int ePos, IntCharFunction<String> toString, Integer maxLength)
1029    {
1030        LV              l   = new LV(sPos, ePos, arr);
1031        StringBuilder   sb  = new StringBuilder();
1032        int             i   = l.start;
1033        String          s;
1034
1035        if (toString == null)
1036        {
1037            sb.append(arr[i++]);
1038            while (i < l.end) sb.append(", " + arr[i++]);
1039        }
1040
1041        else
1042        {
1043            for (i=l.start; i < l.end; i++)
1044                if ((s = toString.apply(i, arr[i])).length() > 0)
1045                    { sb.append(s); break; }
1046
1047            for (i++; i < l.end; i++)
1048                if ((s = toString.apply(i, arr[i])) == null)    throwNPE(i, arr[i]);
1049                else                                            sb.append(", " + s);
1050        }
1051
1052        if ((maxLength != null) && (sb.length() > maxLength.intValue()))
1053        {
1054            sb.setLength(maxLength - 4);
1055            sb.append(" ...");
1056
1057            return sb.toString();
1058        }
1059    
1060        return sb.toString();
1061    }
1062
1063
1064    // ********************************************************************************************
1065    // ********************************************************************************************
1066    // Two Dimensional Primitive Arrays
1067    // ********************************************************************************************
1068    // ********************************************************************************************
1069
1070
1071    /**
1072     * <EMBED CLASS=defs DATA-Type=byte>
1073     * This class prints a two dimensional {@code byte}-array to a {@code String}.
1074     * 
1075     * @param arr            <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_ARR_2D>
1076     * @param toString       <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_TSTR_2DARR>
1077     * @param keepRow        <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_SUB_PRED>
1078     * @param separateLines  <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_SEPL_LINE>
1079     * @param maxLengthInner <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAX_LEN_IN>
1080     * @param maxLengthOuter <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAXLEN_OUT>
1081     * @return               A printed version of this two-dimensional array.
1082     * 
1083     * @throws NullPointerException If the {@code 'toString'} method is non-null, but returns a
1084     * null value.
1085     * @throws IllegalArgumentException If {@code 'maxLengthInner'} is less than {@code '7'}.
1086     */
1087    public static String toCSV(
1088            byte[][] arr, IntIntByteFunc<String> toString, IntPredicate keepRow,
1089            boolean separateLines, Integer maxLengthInner, Integer maxLengthOuter
1090        )
1091    {
1092        // maxLengthInner *MUST* be >= 7, since minimum-string-length is "  [...]"
1093        // A value of 0 to 6 must throw an exception.
1094        // A negative value is treated the same as 'null'
1095
1096        if ((maxLengthInner != null) && (maxLengthInner < 7) && (maxLengthInner >= 0))
1097
1098            throw new IllegalArgumentException
1099                (IAE_MESSAGE_MAXLENINNER.replace("TOK", maxLengthInner.toString()));
1100
1101        else if ((maxLengthInner != null) && (maxLengthInner < 0)) maxLengthInner = null;
1102
1103        StringBuilder   sbOuter = new StringBuilder();  // Primary StringBuilder
1104        StringBuilder   sbInner = new StringBuilder();  // StrinBuilder for the sub/inner arrays
1105        String          s       = null;                 // temp variable
1106        int             i       = 0;                    // outer-loop loop-counter
1107        int             numRows = 0;                    // Count sub-arrays
1108
1109        // If the value passed to 'maxLengthOuter' is negative, treat it the same as 'null'
1110        // SPECIFICALLY: No Max Length.
1111
1112        if ((maxLengthOuter != null) && (maxLengthOuter < 0)) maxLengthOuter = null;
1113
1114        sbOuter.append('[');
1115
1116        for (i=0; i < arr.length; i++)
1117        {
1118            // The "Keep Row" Predicate is used to check whether a sub-array should even be
1119            // included at all.  If "Keep Row" exists, and it rejects the outer array index,
1120            // then the entire row itself should be skipped.
1121
1122            if ((keepRow != null) && (! keepRow.test(i))) continue;
1123
1124            numRows++;
1125            if ((maxLengthOuter != null) && (numRows > maxLengthOuter)) break;
1126
1127            int j = 0;
1128
1129            // System.out.println("end: " + end + ",\ti: " + i + ",\tj: " + j);
1130            // The purpose to this sub-loop is such that there is a "provided user option" where
1131            // the 'toString-lambda' may return a ZERO-LENGTH-STRING, and when this happens, the
1132            // array-location that resulted in a ZERO-LENGTH-STRING shall be ignored/skipped
1133            // completely.
1134
1135            if (toString != null)
1136
1137                while (     (j < arr[i].length)
1138                        && ((s = toString.apply(i, j, arr[i][j])).length() == 0)
1139                )
1140                    j++;
1141
1142            else s = "" + arr[i][j];
1143
1144            // If "separateLines" be sure to add a newline.
1145            if (separateLines) sbOuter.append('\n');
1146
1147            // Add the opening brackets to this new sub-array that was just computed.
1148            sbInner.append(" [" + s);
1149            j++;
1150
1151            // Main Printing Loop
1152            while (j < arr[i].length)
1153            {
1154                if (toString != null)
1155                {
1156                    if ((s = toString.apply(i, j, arr[i][j++])).length() > 0)
1157                        sbInner.append(", " + s);
1158                }
1159
1160                else sbInner.append(", " + arr[i][j++]);
1161
1162                if ((maxLengthInner != null) && (sbInner.length() > maxLengthInner)) break;
1163            }
1164
1165            // NOTE: The '+ 1' is needed because, as of yet, the trailing ']' has not been added.
1166            if ((maxLengthInner != null) && ((sbInner.length() + 1) > maxLengthInner))
1167            {
1168                int     pos = sbInner.length() - 4;
1169                char    c   = sbInner.charAt(pos);
1170
1171                while ((c != ',') && (c != '[')) c = sbInner.charAt(--pos);
1172
1173                sbInner.setLength(pos+1);
1174                sbInner.append((c == '[') ? "..." : " ...");
1175            }
1176
1177            sbInner.append(']');
1178            sbOuter.append(sbInner.toString());
1179            sbInner.setLength(0);
1180        }
1181
1182        // This shall only execute if the 
1183        if (i < arr.length) sbOuter.append((separateLines ? "\n" : "") + "  ...");
1184
1185        sbOuter.append(separateLines ? "\n]" : " ]");
1186    
1187        return sbOuter.toString();
1188    }
1189
1190    /**
1191     * <EMBED CLASS=defs DATA-Type=short>
1192     * This class prints a two dimensional {@code short}-array to a {@code String}.
1193     * 
1194     * @param arr            <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_ARR_2D>
1195     * @param toString       <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_TSTR_2DARR>
1196     * @param keepRow        <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_SUB_PRED>
1197     * @param separateLines  <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_SEPL_LINE>
1198     * @param maxLengthInner <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAX_LEN_IN>
1199     * @param maxLengthOuter <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAXLEN_OUT>
1200     * @return               A printed version of this two-dimensional array.
1201     * 
1202     * @throws NullPointerException If the {@code 'toString'} method is non-null, but returns a
1203     * null value.
1204     * 
1205     * @throws IllegalArgumentException If {@code 'maxLengthInner'} is less than {@code '7'}.
1206     */
1207    public static String toCSV(
1208            short[][] arr, IntIntShortFunc<String> toString, IntPredicate keepRow,
1209            boolean separateLines, Integer maxLengthInner, Integer maxLengthOuter
1210        )
1211    {
1212        // maxLengthInner *MUST* be >= 7, since minimum-string-length is "  [...]"
1213        // A value of 0 to 6 must throw an exception.
1214        // A negative value is treated the same as 'null'
1215
1216        if ((maxLengthInner != null) && (maxLengthInner < 7) && (maxLengthInner >= 0))
1217
1218            throw new IllegalArgumentException
1219                (IAE_MESSAGE_MAXLENINNER.replace("TOK", maxLengthInner.toString()));
1220
1221        else if ((maxLengthInner != null) && (maxLengthInner < 0)) maxLengthInner = null;
1222
1223        StringBuilder   sbOuter = new StringBuilder();  // Primary StringBuilder
1224        StringBuilder   sbInner = new StringBuilder();  // StrinBuilder for the sub/inner arrays
1225        String          s       = null;                 // temp variable
1226        int             i       = 0;                    // outer-loop loop-counter
1227        int             numRows = 0;                    // Count sub-arrays
1228
1229        // If the value passed to 'maxLengthOuter' is negative, treat it the same as 'null'
1230        // SPECIFICALLY: No Max Length.
1231
1232        if ((maxLengthOuter != null) && (maxLengthOuter < 0)) maxLengthOuter = null;
1233
1234        sbOuter.append('[');
1235
1236        for (i=0; i < arr.length; i++)
1237        {
1238            // The "Keep Row" Predicate is used to check whether a sub-array should even be
1239            // included at all.  If "Keep Row" exists, and it rejects the outer array index,
1240            // then the entire row itself should be skipped.
1241
1242            if ((keepRow != null) && (! keepRow.test(i))) continue;
1243
1244            numRows++;
1245            if ((maxLengthOuter != null) && (numRows > maxLengthOuter)) break;
1246
1247            int j = 0;
1248
1249            // System.out.println("end: " + end + ",\ti: " + i + ",\tj: " + j);
1250            // The purpose to this sub-loop is such that there is a "provided user option" where
1251            // the 'toString-lambda' may return a ZERO-LENGTH-STRING, and when this happens, the
1252            // array-location that resulted in a ZERO-LENGTH-STRING shall be ignored/skipped
1253            // completely.
1254
1255            if (toString != null)
1256
1257                while (     (j < arr[i].length)
1258                        && ((s = toString.apply(i, j, arr[i][j])).length() == 0)
1259                )
1260                    j++;
1261
1262            else s = "" + arr[i][j];
1263
1264            // If "separateLines" be sure to add a newline.
1265            if (separateLines) sbOuter.append('\n');
1266
1267            // Add the opening brackets to this new sub-array that was just computed.
1268            sbInner.append(" [" + s);
1269            j++;
1270
1271            // Main Printing Loop
1272            while (j < arr[i].length)
1273            {
1274                if (toString != null)
1275                {
1276                    if ((s = toString.apply(i, j, arr[i][j++])).length() > 0)
1277                        sbInner.append(", " + s);
1278                }
1279
1280                else sbInner.append(", " + arr[i][j++]);
1281
1282                if ((maxLengthInner != null) && (sbInner.length() > maxLengthInner)) break;
1283            }
1284
1285            // NOTE: The '+ 1' is needed because, as of yet, the trailing ']' has not been added.
1286            if ((maxLengthInner != null) && ((sbInner.length() + 1) > maxLengthInner))
1287            {
1288                int     pos = sbInner.length() - 4;
1289                char    c   = sbInner.charAt(pos);
1290
1291                while ((c != ',') && (c != '[')) c = sbInner.charAt(--pos);
1292
1293                sbInner.setLength(pos+1);
1294                sbInner.append((c == '[') ? "..." : " ...");
1295            }
1296
1297            sbInner.append(']');
1298            sbOuter.append(sbInner.toString());
1299            sbInner.setLength(0);
1300        }
1301
1302        // This shall only execute if the 
1303        if (i < arr.length) sbOuter.append((separateLines ? "\n" : "") + "  ...");
1304
1305        sbOuter.append(separateLines ? "\n]" : " ]");
1306    
1307        return sbOuter.toString();
1308    }
1309
1310    /**
1311     * <EMBED CLASS=defs DATA-Type=int>
1312     * This class prints a two dimensional {@code int}-array to a {@code String}.
1313     * 
1314     * @param arr            <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_ARR_2D>
1315     * @param toString       <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_TSTR_2DARR>
1316     * @param keepRow        <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_SUB_PRED>
1317     * @param separateLines  <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_SEPL_LINE>
1318     * @param maxLengthInner <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAX_LEN_IN>
1319     * @param maxLengthOuter <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAXLEN_OUT>
1320     * @return               A printed version of this two-dimensional array.
1321     * 
1322     * @throws NullPointerException If the {@code 'toString'} method is non-null, but returns a
1323     * null value.
1324     * 
1325     * @throws IllegalArgumentException If {@code 'maxLengthInner'} is less than {@code '7'}.
1326     */
1327    public static String toCSV(
1328            int[][] arr, TriIntFunc<String> toString, IntPredicate keepRow,
1329            boolean separateLines, Integer maxLengthInner, Integer maxLengthOuter
1330        )
1331    {
1332        // maxLengthInner *MUST* be >= 7, since minimum-string-length is "  [...]"
1333        // A value of 0 to 6 must throw an exception.
1334        // A negative value is treated the same as 'null'
1335
1336        if ((maxLengthInner != null) && (maxLengthInner < 7) && (maxLengthInner >= 0))
1337
1338            throw new IllegalArgumentException
1339                (IAE_MESSAGE_MAXLENINNER.replace("TOK", maxLengthInner.toString()));
1340
1341        else if ((maxLengthInner != null) && (maxLengthInner < 0)) maxLengthInner = null;
1342
1343        StringBuilder   sbOuter = new StringBuilder();  // Primary StringBuilder
1344        StringBuilder   sbInner = new StringBuilder();  // StrinBuilder for the sub/inner arrays
1345        String          s       = null;                 // temp variable
1346        int             i       = 0;                    // outer-loop loop-counter
1347        int             numRows = 0;                    // Count sub-arrays
1348
1349        // If the value passed to 'maxLengthOuter' is negative, treat it the same as 'null'
1350        // SPECIFICALLY: No Max Length.
1351
1352        if ((maxLengthOuter != null) && (maxLengthOuter < 0)) maxLengthOuter = null;
1353
1354        sbOuter.append('[');
1355
1356        for (i=0; i < arr.length; i++)
1357        {
1358            // The "Keep Row" Predicate is used to check whether a sub-array should even be
1359            // included at all.  If "Keep Row" exists, and it rejects the outer array index,
1360            // then the entire row itself should be skipped.
1361
1362            if ((keepRow != null) && (! keepRow.test(i))) continue;
1363
1364            numRows++;
1365            if ((maxLengthOuter != null) && (numRows > maxLengthOuter)) break;
1366
1367            int j = 0;
1368
1369            // System.out.println("end: " + end + ",\ti: " + i + ",\tj: " + j);
1370            // The purpose to this sub-loop is such that there is a "provided user option" where
1371            // the 'toString-lambda' may return a ZERO-LENGTH-STRING, and when this happens, the
1372            // array-location that resulted in a ZERO-LENGTH-STRING shall be ignored/skipped
1373            // completely.
1374
1375            if (toString != null)
1376
1377                while (     (j < arr[i].length)
1378                        && ((s = toString.apply(i, j, arr[i][j])).length() == 0)
1379                )
1380                    j++;
1381
1382            else s = "" + arr[i][j];
1383
1384            // If "separateLines" be sure to add a newline.
1385            if (separateLines) sbOuter.append('\n');
1386
1387            // Add the opening brackets to this new sub-array that was just computed.
1388            sbInner.append(" [" + s);
1389            j++;
1390
1391            // Main Printing Loop
1392            while (j < arr[i].length)
1393            {
1394                if (toString != null)
1395                {
1396                    if ((s = toString.apply(i, j, arr[i][j++])).length() > 0)
1397                        sbInner.append(", " + s);
1398                }
1399
1400                else sbInner.append(", " + arr[i][j++]);
1401
1402                if ((maxLengthInner != null) && (sbInner.length() > maxLengthInner)) break;
1403            }
1404
1405            // NOTE: The '+ 1' is needed because, as of yet, the trailing ']' has not been added.
1406            if ((maxLengthInner != null) && ((sbInner.length() + 1) > maxLengthInner))
1407            {
1408                int     pos = sbInner.length() - 4;
1409                char    c   = sbInner.charAt(pos);
1410
1411                while ((c != ',') && (c != '[')) c = sbInner.charAt(--pos);
1412
1413                sbInner.setLength(pos+1);
1414                sbInner.append((c == '[') ? "..." : " ...");
1415            }
1416
1417            sbInner.append(']');
1418            sbOuter.append(sbInner.toString());
1419            sbInner.setLength(0);
1420        }
1421
1422        // This shall only execute if the 
1423        if (i < arr.length) sbOuter.append((separateLines ? "\n" : "") + "  ...");
1424
1425        sbOuter.append(separateLines ? "\n]" : " ]");
1426    
1427        return sbOuter.toString();
1428    }
1429
1430    /**
1431     * <EMBED CLASS=defs DATA-Type=long>
1432     * This class prints a two dimensional {@code long}-array to a {@code String}.
1433     * 
1434     * @param arr            <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_ARR_2D>
1435     * @param toString       <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_TSTR_2DARR>
1436     * @param keepRow        <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_SUB_PRED>
1437     * @param separateLines  <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_SEPL_LINE>
1438     * @param maxLengthInner <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAX_LEN_IN>
1439     * @param maxLengthOuter <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAXLEN_OUT>
1440     * @return               A printed version of this two-dimensional array.
1441     * 
1442     * @throws NullPointerException If the {@code 'toString'} method is non-null, but returns a
1443     * null value.
1444     * 
1445     * @throws IllegalArgumentException If {@code 'maxLengthInner'} is less than {@code '7'}.
1446     */
1447    public static String toCSV(
1448            long[][] arr, IntIntLongFunc<String> toString, IntPredicate keepRow,
1449            boolean separateLines, Integer maxLengthInner, Integer maxLengthOuter
1450        )
1451    {
1452        // maxLengthInner *MUST* be >= 7, since minimum-string-length is "  [...]"
1453        // A value of 0 to 6 must throw an exception.
1454        // A negative value is treated the same as 'null'
1455
1456        if ((maxLengthInner != null) && (maxLengthInner < 7) && (maxLengthInner >= 0))
1457
1458            throw new IllegalArgumentException
1459                (IAE_MESSAGE_MAXLENINNER.replace("TOK", maxLengthInner.toString()));
1460
1461        else if ((maxLengthInner != null) && (maxLengthInner < 0)) maxLengthInner = null;
1462
1463        StringBuilder   sbOuter = new StringBuilder();  // Primary StringBuilder
1464        StringBuilder   sbInner = new StringBuilder();  // StrinBuilder for the sub/inner arrays
1465        String          s       = null;                 // temp variable
1466        int             i       = 0;                    // outer-loop loop-counter
1467        int             numRows = 0;                    // Count sub-arrays
1468
1469        // If the value passed to 'maxLengthOuter' is negative, treat it the same as 'null'
1470        // SPECIFICALLY: No Max Length.
1471
1472        if ((maxLengthOuter != null) && (maxLengthOuter < 0)) maxLengthOuter = null;
1473
1474        sbOuter.append('[');
1475
1476        for (i=0; i < arr.length; i++)
1477        {
1478            // The "Keep Row" Predicate is used to check whether a sub-array should even be
1479            // included at all.  If "Keep Row" exists, and it rejects the outer array index,
1480            // then the entire row itself should be skipped.
1481
1482            if ((keepRow != null) && (! keepRow.test(i))) continue;
1483
1484            numRows++;
1485            if ((maxLengthOuter != null) && (numRows > maxLengthOuter)) break;
1486
1487            int j = 0;
1488
1489            // System.out.println("end: " + end + ",\ti: " + i + ",\tj: " + j);
1490            // The purpose to this sub-loop is such that there is a "provided user option" where
1491            // the 'toString-lambda' may return a ZERO-LENGTH-STRING, and when this happens, the
1492            // array-location that resulted in a ZERO-LENGTH-STRING shall be ignored/skipped
1493            // completely.
1494
1495            if (toString != null)
1496
1497                while (     (j < arr[i].length)
1498                        && ((s = toString.apply(i, j, arr[i][j])).length() == 0)
1499                )
1500                    j++;
1501
1502            else s = "" + arr[i][j];
1503
1504            // If "separateLines" be sure to add a newline.
1505            if (separateLines) sbOuter.append('\n');
1506
1507            // Add the opening brackets to this new sub-array that was just computed.
1508            sbInner.append(" [" + s);
1509            j++;
1510
1511            // Main Printing Loop
1512            while (j < arr[i].length)
1513            {
1514                if (toString != null)
1515                {
1516                    if ((s = toString.apply(i, j, arr[i][j++])).length() > 0)
1517                        sbInner.append(", " + s);
1518                }
1519
1520                else sbInner.append(", " + arr[i][j++]);
1521
1522                if ((maxLengthInner != null) && (sbInner.length() > maxLengthInner)) break;
1523            }
1524
1525            // NOTE: The '+ 1' is needed because, as of yet, the trailing ']' has not been added.
1526            if ((maxLengthInner != null) && ((sbInner.length() + 1) > maxLengthInner))
1527            {
1528                int     pos = sbInner.length() - 4;
1529                char    c   = sbInner.charAt(pos);
1530
1531                while ((c != ',') && (c != '[')) c = sbInner.charAt(--pos);
1532
1533                sbInner.setLength(pos+1);
1534                sbInner.append((c == '[') ? "..." : " ...");
1535            }
1536
1537            sbInner.append(']');
1538            sbOuter.append(sbInner.toString());
1539            sbInner.setLength(0);
1540        }
1541
1542        // This shall only execute if the 
1543        if (i < arr.length) sbOuter.append((separateLines ? "\n" : "") + "  ...");
1544
1545        sbOuter.append(separateLines ? "\n]" : " ]");
1546    
1547        return sbOuter.toString();
1548    }
1549
1550    /**
1551     * <EMBED CLASS=defs DATA-Type=float>
1552     * This class prints a two dimensional {@code float}-array to a {@code String}.
1553     * 
1554     * @param arr            <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_ARR_2D>
1555     * @param toString       <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_TSTR_2DARR>
1556     * @param keepRow        <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_SUB_PRED>
1557     * @param separateLines  <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_SEPL_LINE>
1558     * @param maxLengthInner <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAX_LEN_IN>
1559     * @param maxLengthOuter <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAXLEN_OUT>
1560     * @return               A printed version of this two-dimensional array.
1561     * 
1562     * @throws NullPointerException If the {@code 'toString'} method is non-null, but returns a
1563     * null value.
1564     * 
1565     * @throws IllegalArgumentException If {@code 'maxLengthInner'} is less than {@code '7'}.
1566     */
1567    public static String toCSV(
1568            float[][] arr, IntIntFloatFunc<String> toString, IntPredicate keepRow,
1569            boolean separateLines, Integer maxLengthInner, Integer maxLengthOuter
1570        )
1571    {
1572        // maxLengthInner *MUST* be >= 7, since minimum-string-length is "  [...]"
1573        // A value of 0 to 6 must throw an exception.
1574        // A negative value is treated the same as 'null'
1575
1576        if ((maxLengthInner != null) && (maxLengthInner < 7) && (maxLengthInner >= 0))
1577
1578            throw new IllegalArgumentException
1579                (IAE_MESSAGE_MAXLENINNER.replace("TOK", maxLengthInner.toString()));
1580
1581        else if ((maxLengthInner != null) && (maxLengthInner < 0)) maxLengthInner = null;
1582
1583        StringBuilder   sbOuter = new StringBuilder();  // Primary StringBuilder
1584        StringBuilder   sbInner = new StringBuilder();  // StrinBuilder for the sub/inner arrays
1585        String          s       = null;                 // temp variable
1586        int             i       = 0;                    // outer-loop loop-counter
1587        int             numRows = 0;                    // Count sub-arrays
1588
1589        // If the value passed to 'maxLengthOuter' is negative, treat it the same as 'null'
1590        // SPECIFICALLY: No Max Length.
1591
1592        if ((maxLengthOuter != null) && (maxLengthOuter < 0)) maxLengthOuter = null;
1593
1594        sbOuter.append('[');
1595
1596        for (i=0; i < arr.length; i++)
1597        {
1598            // The "Keep Row" Predicate is used to check whether a sub-array should even be
1599            // included at all.  If "Keep Row" exists, and it rejects the outer array index,
1600            // then the entire row itself should be skipped.
1601
1602            if ((keepRow != null) && (! keepRow.test(i))) continue;
1603
1604            numRows++;
1605            if ((maxLengthOuter != null) && (numRows > maxLengthOuter)) break;
1606
1607            int j = 0;
1608
1609            // System.out.println("end: " + end + ",\ti: " + i + ",\tj: " + j);
1610            // The purpose to this sub-loop is such that there is a "provided user option" where
1611            // the 'toString-lambda' may return a ZERO-LENGTH-STRING, and when this happens, the
1612            // array-location that resulted in a ZERO-LENGTH-STRING shall be ignored/skipped
1613            // completely.
1614
1615            if (toString != null)
1616
1617                while (     (j < arr[i].length)
1618                        && ((s = toString.apply(i, j, arr[i][j])).length() == 0)
1619                )
1620                    j++;
1621
1622            else s = "" + arr[i][j];
1623
1624            // If "separateLines" be sure to add a newline.
1625            if (separateLines) sbOuter.append('\n');
1626
1627            // Add the opening brackets to this new sub-array that was just computed.
1628            sbInner.append(" [" + s);
1629            j++;
1630
1631            // Main Printing Loop
1632            while (j < arr[i].length)
1633            {
1634                if (toString != null)
1635                {
1636                    if ((s = toString.apply(i, j, arr[i][j++])).length() > 0)
1637                        sbInner.append(", " + s);
1638                }
1639
1640                else sbInner.append(", " + arr[i][j++]);
1641
1642                if ((maxLengthInner != null) && (sbInner.length() > maxLengthInner)) break;
1643            }
1644
1645            // NOTE: The '+ 1' is needed because, as of yet, the trailing ']' has not been added.
1646            if ((maxLengthInner != null) && ((sbInner.length() + 1) > maxLengthInner))
1647            {
1648                int     pos = sbInner.length() - 4;
1649                char    c   = sbInner.charAt(pos);
1650
1651                while ((c != ',') && (c != '[')) c = sbInner.charAt(--pos);
1652
1653                sbInner.setLength(pos+1);
1654                sbInner.append((c == '[') ? "..." : " ...");
1655            }
1656
1657            sbInner.append(']');
1658            sbOuter.append(sbInner.toString());
1659            sbInner.setLength(0);
1660        }
1661
1662        // This shall only execute if the 
1663        if (i < arr.length) sbOuter.append((separateLines ? "\n" : "") + "  ...");
1664
1665        sbOuter.append(separateLines ? "\n]" : " ]");
1666    
1667        return sbOuter.toString();
1668    }
1669
1670    /**
1671     * <EMBED CLASS=defs DATA-Type=double>
1672     * This class prints a two dimensional {@code double}-array to a {@code String}.
1673     * 
1674     * @param arr            <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_ARR_2D>
1675     * @param toString       <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_TSTR_2DARR>
1676     * @param keepRow        <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_SUB_PRED>
1677     * @param separateLines  <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_SEPL_LINE>
1678     * @param maxLengthInner <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAX_LEN_IN>
1679     * @param maxLengthOuter <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAXLEN_OUT>
1680     * @return               A printed version of this two-dimensional array.
1681     * 
1682     * @throws NullPointerException If the {@code 'toString'} method is non-null, but returns a
1683     * null value.
1684     * 
1685     * @throws IllegalArgumentException If {@code 'maxLengthInner'} is less than {@code '7'}.
1686     */
1687    public static String toCSV(
1688            double[][] arr, IntIntDoubleFunc<String> toString, IntPredicate keepRow,
1689            boolean separateLines, Integer maxLengthInner, Integer maxLengthOuter
1690        )
1691    {
1692        // maxLengthInner *MUST* be >= 7, since minimum-string-length is "  [...]"
1693        // A value of 0 to 6 must throw an exception.
1694        // A negative value is treated the same as 'null'
1695
1696        if ((maxLengthInner != null) && (maxLengthInner < 7) && (maxLengthInner >= 0))
1697
1698            throw new IllegalArgumentException
1699                (IAE_MESSAGE_MAXLENINNER.replace("TOK", maxLengthInner.toString()));
1700
1701        else if ((maxLengthInner != null) && (maxLengthInner < 0)) maxLengthInner = null;
1702
1703        StringBuilder   sbOuter = new StringBuilder();  // Primary StringBuilder
1704        StringBuilder   sbInner = new StringBuilder();  // StrinBuilder for the sub/inner arrays
1705        String          s       = null;                 // temp variable
1706        int             i       = 0;                    // outer-loop loop-counter
1707        int             numRows = 0;                    // Count sub-arrays
1708
1709        // If the value passed to 'maxLengthOuter' is negative, treat it the same as 'null'
1710        // SPECIFICALLY: No Max Length.
1711        if ((maxLengthOuter != null) && (maxLengthOuter < 0)) maxLengthOuter = null;
1712
1713        sbOuter.append('[');
1714
1715        for (i=0; i < arr.length; i++)
1716        {
1717            // The "Keep Row" Predicate is used to check whether a sub-array should even be
1718            // included at all.  If "Keep Row" exists, and it rejects the outer array index,
1719            // then the entire row itself should be skipped.
1720
1721            if ((keepRow != null) && (! keepRow.test(i))) continue;
1722
1723            numRows++;
1724            if ((maxLengthOuter != null) && (numRows > maxLengthOuter)) break;
1725
1726            int j = 0;
1727
1728            // System.out.println("end: " + end + ",\ti: " + i + ",\tj: " + j);
1729            // The purpose to this sub-loop is such that there is a "provided user option" where
1730            // the 'toString-lambda' may return a ZERO-LENGTH-STRING, and when this happens, the
1731            // array-location that resulted in a ZERO-LENGTH-STRING shall be ignored/skipped
1732            // completely.
1733
1734            if (toString != null)
1735
1736                while (     (j < arr[i].length)
1737                        && ((s = toString.apply(i, j, arr[i][j])).length() == 0)
1738                )
1739                    j++;
1740
1741            else s = "" + arr[i][j];
1742
1743            // If "separateLines" be sure to add a newline.
1744            if (separateLines) sbOuter.append('\n');
1745
1746            // Add the opening brackets to this new sub-array that was just computed.
1747            sbInner.append(" [" + s);
1748            j++;
1749
1750            // Main Printing Loop
1751            while (j < arr[i].length)
1752            {
1753                if (toString != null)
1754                {
1755                    if ((s = toString.apply(i, j, arr[i][j++])).length() > 0)
1756                        sbInner.append(", " + s);
1757                }
1758
1759                else sbInner.append(", " + arr[i][j++]);
1760
1761                if ((maxLengthInner != null) && (sbInner.length() > maxLengthInner)) break;
1762            }
1763
1764            // NOTE: The '+ 1' is needed because, as of yet, the trailing ']' has not been added.
1765            if ((maxLengthInner != null) && ((sbInner.length() + 1) > maxLengthInner))
1766            {
1767                int     pos = sbInner.length() - 4;
1768                char    c   = sbInner.charAt(pos);
1769
1770                while ((c != ',') && (c != '[')) c = sbInner.charAt(--pos);
1771
1772                sbInner.setLength(pos+1);
1773                sbInner.append((c == '[') ? "..." : " ...");
1774            }
1775
1776            sbInner.append(']');
1777            sbOuter.append(sbInner.toString());
1778            sbInner.setLength(0);
1779        }
1780
1781        // This shall only execute if the 
1782        if (i < arr.length) sbOuter.append((separateLines ? "\n" : "") + "  ...");
1783
1784        sbOuter.append(separateLines ? "\n]" : " ]");
1785    
1786        return sbOuter.toString();
1787    }
1788
1789    /**
1790     * <EMBED CLASS=defs DATA-Type=char>
1791     * This class prints a two dimensional {@code char}-array to a {@code String}.
1792     * 
1793     * @param arr            <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_ARR_2D>
1794     * @param toString       <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_TSTR_2DARR>
1795     * @param keepRow        <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_SUB_PRED>
1796     * @param separateLines  <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_SEPL_LINE>
1797     * @param maxLengthInner <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAX_LEN_IN>
1798     * @param maxLengthOuter <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAXLEN_OUT>
1799     * @return               A printed version of this two-dimensional array.
1800     * 
1801     * @throws NullPointerException If the {@code 'toString'} method is non-null, but returns a
1802     * null value.
1803     * 
1804     * @throws IllegalArgumentException If {@code 'maxLengthInner'} is less than {@code '7'}.
1805     */
1806    public static String toCSV(
1807            char[][] arr, IntIntCharFunc<String> toString, IntPredicate keepRow,
1808            boolean separateLines, Integer maxLengthInner, Integer maxLengthOuter
1809        )
1810    {
1811        // maxLengthInner *MUST* be >= 7, since minimum-string-length is "  [...]"
1812        // A value of 0 to 6 must throw an exception.
1813        // A negative value is treated the same as 'null'
1814
1815        if ((maxLengthInner != null) && (maxLengthInner < 7) && (maxLengthInner >= 0))
1816
1817            throw new IllegalArgumentException
1818                (IAE_MESSAGE_MAXLENINNER.replace("TOK", maxLengthInner.toString()));
1819
1820        else if ((maxLengthInner != null) && (maxLengthInner < 0)) maxLengthInner = null;
1821
1822        StringBuilder   sbOuter = new StringBuilder();  // Primary StringBuilder
1823        StringBuilder   sbInner = new StringBuilder();  // StrinBuilder for the sub/inner arrays
1824        String          s       = null;                 // temp variable
1825        int             i       = 0;                    // outer-loop loop-counter
1826        int             numRows = 0;                    // Count sub-arrays
1827
1828        // If the value passed to 'maxLengthOuter' is negative, treat it the same as 'null'
1829        // SPECIFICALLY: No Max Length.
1830
1831        if ((maxLengthOuter != null) && (maxLengthOuter < 0)) maxLengthOuter = null;
1832
1833        sbOuter.append('[');
1834
1835        for (i=0; i < arr.length; i++)
1836        {
1837            // The "Keep Row" Predicate is used to check whether a sub-array should even be
1838            // included at all.  If "Keep Row" exists, and it rejects the outer array index,
1839            // then the entire row itself should be skipped.
1840
1841            if ((keepRow != null) && (! keepRow.test(i))) continue;
1842
1843            numRows++;
1844            if ((maxLengthOuter != null) && (numRows > maxLengthOuter)) break;
1845
1846            int j = 0;
1847
1848            // System.out.println("end: " + end + ",\ti: " + i + ",\tj: " + j);
1849            // The purpose to this sub-loop is such that there is a "provided user option" where
1850            // the 'toString-lambda' may return a ZERO-LENGTH-STRING, and when this happens, the
1851            // array-location that resulted in a ZERO-LENGTH-STRING shall be ignored/skipped
1852            // completely.
1853
1854            if (toString != null)
1855
1856                while (     (j < arr[i].length)
1857                        && ((s = toString.apply(i, j, arr[i][j])).length() == 0)
1858                )
1859                    j++;
1860
1861            else s = "" + arr[i][j];
1862
1863            // If "separateLines" be sure to add a newline.
1864            if (separateLines) sbOuter.append('\n');
1865
1866            // Add the opening brackets to this new sub-array that was just computed.
1867            sbInner.append(" [" + s);
1868            j++;
1869
1870            // Main Printing Loop
1871            while (j < arr[i].length)
1872            {
1873                if (toString != null)
1874                {
1875                    if ((s = toString.apply(i, j, arr[i][j++])).length() > 0)
1876                        sbInner.append(", " + s);
1877                }
1878
1879                else sbInner.append(", " + arr[i][j++]);
1880
1881                if ((maxLengthInner != null) && (sbInner.length() > maxLengthInner)) break;
1882            }
1883
1884            // NOTE: The '+ 1' is needed because, as of yet, the trailing ']' has not been added.
1885            if ((maxLengthInner != null) && ((sbInner.length() + 1) > maxLengthInner))
1886            {
1887                int     pos = sbInner.length() - 4;
1888                char    c   = sbInner.charAt(pos);
1889
1890                while ((c != ',') && (c != '[')) c = sbInner.charAt(--pos);
1891
1892                sbInner.setLength(pos+1);
1893                sbInner.append((c == '[') ? "..." : " ...");
1894            }
1895
1896            sbInner.append(']');
1897            sbOuter.append(sbInner.toString());
1898            sbInner.setLength(0);
1899        }
1900
1901        // This shall only execute if the 
1902        if (i < arr.length) sbOuter.append((separateLines ? "\n" : "") + "  ...");
1903
1904        sbOuter.append(separateLines ? "\n]" : " ]");
1905    
1906        return sbOuter.toString();
1907    }
1908
1909    /**
1910     * <EMBED CLASS=defs DATA-Type=boolean>
1911     * This class prints a two dimensional {@code boolean}-array to a {@code String}.
1912     * 
1913     * @param arr            <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_ARR_2D>
1914     * @param toString       <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_TSTR_2DARR>
1915     * @param keepRow        <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_SUB_PRED>
1916     * @param separateLines  <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_SEPL_LINE>
1917     * @param maxLengthInner <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAX_LEN_IN>
1918     * @param maxLengthOuter <EMBED CLASS='external-html' DATA-FILE-ID=SCSV_MAXLEN_OUT>
1919     * @return               A printed version of this two-dimensional array.
1920     * 
1921     * @throws NullPointerException If the {@code 'toString'} method is non-null, but returns a
1922     * null value.
1923     * 
1924     * @throws IllegalArgumentException If {@code 'maxLengthInner'} is less than {@code '7'}.
1925     */
1926    public static String toCSV(
1927            boolean[][] arr, IntIntBoolFunc<String> toString, IntPredicate keepRow,
1928            boolean separateLines, Integer maxLengthInner, Integer maxLengthOuter
1929        )
1930    {
1931        // maxLengthInner *MUST* be >= 7, since minimum-string-length is "  [...]"
1932        // A value of 0 to 6 must throw an exception.
1933        // A negative value is treated the same as 'null'
1934
1935        if ((maxLengthInner != null) && (maxLengthInner < 7) && (maxLengthInner >= 0))
1936
1937            throw new IllegalArgumentException
1938                (IAE_MESSAGE_MAXLENINNER.replace("TOK", maxLengthInner.toString()));
1939
1940        else if ((maxLengthInner != null) && (maxLengthInner < 0)) maxLengthInner = null;
1941
1942        StringBuilder   sbOuter = new StringBuilder();  // Primary StringBuilder
1943        StringBuilder   sbInner = new StringBuilder();  // StrinBuilder for the sub/inner arrays
1944        String          s       = null;                 // temp variable
1945        int             i       = 0;                    // outer-loop loop-counter
1946        int             numRows = 0;                    // Count sub-arrays
1947
1948        // If the value passed to 'maxLengthOuter' is negative, treat it the same as 'null'
1949        // SPECIFICALLY: No Max Length.
1950
1951        if ((maxLengthOuter != null) && (maxLengthOuter < 0)) maxLengthOuter = null;
1952
1953        sbOuter.append('[');
1954
1955        for (i=0; i < arr.length; i++)
1956        {
1957            // The "Keep Row" Predicate is used to check whether a sub-array should even be
1958            // included at all.  If "Keep Row" exists, and it rejects the outer array index,
1959            // then the entire row itself should be skipped.
1960
1961            if ((keepRow != null) && (! keepRow.test(i))) continue;
1962
1963            numRows++;
1964            if ((maxLengthOuter != null) && (numRows > maxLengthOuter)) break;
1965
1966            int j = 0;
1967
1968            // System.out.println("end: " + end + ",\ti: " + i + ",\tj: " + j);
1969            // The purpose to this sub-loop is such that there is a "provided user option" where
1970            // the 'toString-lambda' may return a ZERO-LENGTH-STRING, and when this happens, the
1971            // array-location that resulted in a ZERO-LENGTH-STRING shall be ignored/skipped
1972            // completely.
1973
1974            if (toString != null)
1975
1976                while (     (j < arr[i].length)
1977                        && ((s = toString.apply(i, j, arr[i][j])).length() == 0)
1978                )
1979                    j++;
1980
1981            else s = "" + arr[i][j];
1982
1983            // If "separateLines" be sure to add a newline.
1984            if (separateLines) sbOuter.append('\n');
1985
1986            // Add the opening brackets to this new sub-array that was just computed.
1987            sbInner.append(" [" + s);
1988            j++;
1989
1990            // Main Printing Loop
1991            while (j < arr[i].length)
1992            {
1993                if (toString != null)
1994                {
1995                    if ((s = toString.apply(i, j, arr[i][j++])).length() > 0)
1996                        sbInner.append(", " + s);
1997                }
1998
1999                else sbInner.append(", " + arr[i][j++]);
2000
2001                if ((maxLengthInner != null) && (sbInner.length() > maxLengthInner)) break;
2002            }
2003
2004            // NOTE: The '+ 1' is needed because, as of yet, the trailing ']' has not been added.
2005            if ((maxLengthInner != null) && ((sbInner.length() + 1) > maxLengthInner))
2006            {
2007                int     pos = sbInner.length() - 4;
2008                char    c   = sbInner.charAt(pos);
2009
2010                while ((c != ',') && (c != '[')) c = sbInner.charAt(--pos);
2011
2012                sbInner.setLength(pos+1);
2013                sbInner.append((c == '[') ? "..." : " ...");
2014            }
2015
2016            sbInner.append(']');
2017            sbOuter.append(sbInner.toString());
2018            sbInner.setLength(0);
2019        }
2020
2021        // This shall only execute if the 
2022        if (i < arr.length) sbOuter.append((separateLines ? "\n" : "") + "  ...");
2023
2024        sbOuter.append(separateLines ? "\n]" : " ]");
2025    
2026        return sbOuter.toString();
2027    }
2028}