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