001package Torello.Java;
002
003import java.util.function.Consumer;
004import java.util.function.Predicate;
005import java.util.Vector;
006
007import Torello.JavaDoc.StaticFunctional;
008import Torello.JavaDoc.Excuse;
009
010/**
011 * Class String-Compare provides an exhaustive-combinatoric suite of methods that extend the
012 * basic Java <CODE>String</CODE> methods <CODE>equals, contains, startsWith</CODE> and
013 * <CODE>endsWith</CODE>.
014 * 
015 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_CMPR>
016 */
017@StaticFunctional(Excused={"DEBUG", "DEBUG_LOG"}, Excuses={Excuse.DEBUGGING, Excuse.DEBUGGING})
018public class StrCmpr
019{
020    private StrCmpr() { }
021
022    /**
023     * Utility field.  You may choose to set this variable to true, and the following
024     * {@code String} commands will print to  an internally stored {@code Consumer<String> DEBUG_LOG}
025     * class that may be set.  This is generally a very minor drain on code-efficiency.  When this
026     * flag is set to {@code FALSE}, a short {@code if-statement evaluation} <I>still occurs even
027     * when the flag is false</I> on each occasion that the string-comparison loops identify and
028     * return a match.  This is very minor performance loss, and does provide quite a lot of help
029     * to those trying to identify difficult to notice problems with partial-{@code String}
030     * comparisons.
031     * 
032     * <BR /><BR /><B CLASS=JDDescLabel>Required Setting:</B>
033     * 
034     * <BR />In order to use this minor Debugging Feature, it is necessary to provide a 
035     * {@code Consumer<String>} to public field {@link #DEBUG_LOG}!  This field is a {@code public}
036     * and {@code static} field, which will be used by any invocation of the methods in this class.
037     * This {@code String}-consumer may do anything you would like it to do with the provided
038     * Debug-{@code String} data.
039     *
040     * <BR /><BR /><B CLASS=JDDescLabel>String-Format:</B>
041     * 
042     * <BR />The {@code String} that is ultimately sent to the {@code Consumer<String>} you provide
043     * will be formatted, as below, in the following Code-Snippet:
044     * 
045     * <BR /><DIV CLASS=SNIP>{@code
046     * private static void PRNT(String methodName, String srcStr, String compareStr)
047     * { DEBUG_LOG.accept(methodName + "-MATCH[" + srcStr + ", " + compareStr + "] "); }
048     * }</DIV>
049     * 
050     * <BR /><BR />Generally, you would assign a writer that looks like something the Lambda-Target
051     * / Function-Pointer assignment in the snippet below:
052     * 
053     * <BR /><DIV CLASS=SNIP>{@code
054     * StrCmpr.DEBUG_LOG = (String logInfoStr) -> System.out.println(logInfoStr);
055     * }</DIV>
056     *
057     * <BR /><BR />Finally, if you are using this field, please note that any of the methods whose
058     * name ends with the phrase "IgnoreCase" <I>will not print to the {@link #DEBUG_LOG}</I>.
059     * This is primarily because these are all <I>single-argument comparisons</I>, and logging
060     * would be of only minor benefit.
061     * 
062     * <BR /><BR />The primary value of a debug-log is the ability to identify whether / when a
063     * particular substring from a list of substrings actually matched.  
064     */
065    public static boolean DEBUG = false;
066
067    /**
068     * This object reference <I><B>cannot remain null when the field {@link #DEBUG} is set to
069     * {@code TRUE}</B></I>.  If you have turned {@link #DEBUG} on (by setting the field to 
070     * {@code TRUE}), and this is null, then a {@code NullPointerException} will be thrown on the
071     * very next invocation of any of the methods in this class.
072     *
073     * <BR /><BR /><B CLASS=JDDescLabel>DEBUG_LOG is not Thread-Safe:</B>
074     * 
075     * <BR />No attempt has been made to ensure that this debugging "feature" will operate
076     * perfectly in a multi-threaded environment.  The two reasons for this are:
077     *
078     * <BR /><BR /><OL CLASS=JDOL>
079     * <LI> The primary purpose of this LOG is for debugging code, not putting details about
080     *      string-match information into an 'on-line' or production environment.
081     *      <BR /><BR />
082     *      </LI>
083     * 
084     * <LI> This is a best-efforts string-comparison package that would sacrifice quite a bit of its
085     *      utility if it were expected to maintain multiple instances of this class just to have
086     *      {@code StrCmpr} debug operations work in multiple-threads.  Code readability necessitates
087     *      keeping this a class with only <B><I>static methods.</I></B>
088     *      <BR /><BR />
089     *      </LI>
090     *
091     * <LI> Two threads making calls to this class {@code StrCmpr} might see log-writes that, sadly,
092     *      look like they 'interlaced' (crashed), but even when this occasions, reading the log
093     *      wouldn't be that difficult anyway.
094     * </LI>
095     * </OL>
096     */
097    public static Consumer<String> DEBUG_LOG = null;
098
099    private static void PRNT(String methodName, String srcStr, String compareStr)
100    { DEBUG_LOG.accept(methodName + srcStr + ", " + compareStr + "] "); }
101
102    /**
103     * This performs the internal AND.  It expects a comparison {@code Predicate} in order for the
104     * comparison to work.
105     * @param methodName If printing-debug information is expected, by the DEBUG global-variable,
106     * this {@code String} is used.
107     * @param srcStr This is the same source-string parameter from all the methods in this class.
108     * @param compareStr This is the same var-args string array from all the methods in this class.
109     * @param pred This is the comparison {@code Predicate} provided by the methods in this class
110     * that call this method.
111     * @return The AND of these {@code String's}, using the provided {@code Predicate}.
112     * @throws StrCmprException This exception shall throw if there are any invalid
113     * {@code String's} in the compare-string parameter array.
114     * <BR /><BR /><SPAN STYLE="color: red;"><B>IMPORTANT NOTE:</SPAN></B> The conditions that
115     * would cause this exception to throw should remind the reader that <I><B>each and every
116     * method here</I></B> will throw exception 'StrCmprException' if invalid input has been
117     * passed to the "Compare String" Variable-Arguments {@code String...} Parameter.
118     */
119    protected static boolean AND
120        (String methodName, String srcStr, String[] compareStr, Predicate<String> pred)
121    {
122        StrCmprException.check(compareStr);
123
124        for (String cmp: compareStr)
125
126            if (! pred.test(cmp))
127            {
128                if (DEBUG) PRNT(methodName + "-MATCHFAIL", srcStr, cmp);
129                return false;
130            }
131
132        return true;
133    }
134
135    /**
136     * This performs the internal NAND.  It expects a comparison {@code Predicate} in order for the
137     * comparison to work.
138     * @param methodName If printing-debug information is expected, by the DEBUG global-variable,
139     * this {@code String} is used.
140     * @param srcStr This is the same source-string parameter from all the methods in this class.
141     * @param compareStr This is the same var-args string array from all the methods in this class.
142     * @param pred This is the comparison {@code Predicate} provided by the methods in this class
143     * that call this method.
144     * @return The NAND of these {@code String's}, using the provided {@code Predicate}.
145     * @throws StrCmprException This exception shall throw if there are any invalid
146     * {@code String's} in the compare-string parameter array.
147     * <BR /><BR /><SPAN STYLE="color: red;"><B>IMPORTANT NOTE:</SPAN></B> The conditions that
148     * would cause this exception to throw should remind the reader that <I><B>each and every
149     * method here</I></B> will throw exception 'StrCmprException' if invalid input has been passed
150     * to the "Compare String" Variable-Arguments {@code String...} Parameter.
151     */
152    protected static boolean NAND
153        (String methodName, String srcStr, String[] compareStr, Predicate<String> pred)
154    {
155        StrCmprException.check(compareStr); 
156
157        for (String cmp: compareStr)
158
159            if (pred.test(cmp))
160            {
161                if (DEBUG) PRNT(methodName + "-MATCH", srcStr, cmp);
162                return false;
163            }
164
165        return true;
166    }
167
168    /**
169     * This performs the internal OR.  It expects a comparison {@code Predicate} in order for the
170     * comparison to work.
171     * @param methodName If printing-debug information is expected, by the DEBUG global-variable,
172     * this {@code String} is used.
173     * @param srcStr This is the same source-string parameter from all the methods in this class.
174     * @param compareStr This is the same var-args string array from all the methods in this class.
175     * @param pred This is the comparison {@code Predicate} provided by the methods in this class
176     * that call this method.
177     * @return The OR of these {@code String's}, using the provided {@code Predicate}.
178     * @throws StrCmprException This exception shall throw if there are any invalid
179     * {@code String's} in the compare-string parameter array.
180     * <BR /><BR /><SPAN STYLE="color: red;"><B>IMPORTANT NOTE:</SPAN></B> The conditions that
181     * would cause this exception to throw should remind the reader that <I><B>each and every
182     * method here</I></B> will throw exception 'StrCmprException' if invalid input has been passed
183     * to the "Compare String" Variable-Arguments {@code String...} Parameter.
184     */
185    protected static boolean OR
186        (String methodName, String srcStr, String[] compareStr, Predicate<String> pred)
187    {
188        StrCmprException.check(compareStr);
189
190        for (String cmp: compareStr)
191
192            if (pred.test(cmp))
193            {
194                if (DEBUG) PRNT(methodName + "-MATCH", srcStr, cmp);
195                return true;
196            }
197
198        return false;
199    }
200
201    /**
202     * This performs the internal XOR.  It expects a comparison {@code Predicate} in order for the
203     * comparison to work.
204     * @param methodName If printing-debug information is expected, by the DEBUG global-variable,
205     * this {@code String} is used.
206     * @param srcStr This is the same source-string parameter from all the methods in this class.
207     * @param compareStr This is the same var-args string array from all the methods in this class.
208     * @param pred This is the comparison {@code Predicate} provided by the methods in this class
209     * that call this method.
210     * @return The XOR of these {@code String's}, using the provided {@code Predicate}.
211     * @throws StrCmprException This exception shall throw if there are any invalid
212     * {@code String's} in the compare-string parameter array.
213     * <BR /><BR /><SPAN STYLE="color: red;"><B>IMPORTANT NOTE:</SPAN></B> The conditions that
214     * would cause this exception to throw should remind the reader that <I><B>each and every
215     * method here</I></B> will throw exception 'StrCmprException' if invalid
216     * input has been passed to the "Compare String" Variable-Arguments {@code String...}
217     * Parameter.
218     */
219    protected static boolean XOR
220        (String methodName, String srcStr, String[] compareStr, Predicate<String> pred)
221    {
222        StrCmprException.check(compareStr); 
223
224        int count=0;
225
226        for (String cmp: compareStr)
227
228            if (pred.test(cmp))
229
230                if (++count > 1)
231                {
232                    if (DEBUG) PRNT(methodName + "-MATCH", srcStr, cmp);
233                    return false;
234                }
235
236        return count == 1;
237    }
238
239
240
241    // ********************************************************************************************
242    // ********************************************************************************************
243    // ********************************************************************************************
244    // ********************************************************************************************
245    // ********************************************************************************************
246    // StrCmpr Main Section #1
247    // ********************************************************************************************
248    // ********************************************************************************************
249    // ********************************************************************************************
250    // ********************************************************************************************
251    // ********************************************************************************************
252
253
254    
255    // ********************************************************************************************
256    // ********************************************************************************************
257    // EQUALS
258    // ********************************************************************************************
259    // ********************************************************************************************
260
261
262    /**
263     * <EMBED CLASS=defs DATA-DESC='equals exactly one' DATA-CI='is'>
264     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC>
265     * @param srcStr Any non-null instance of a {@code java.lang.String}
266     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
267     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET>
268     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCXORNOTE>
269     * @see #XOR(String, String, String[], Predicate)
270     */
271    public static boolean equalsXOR(String srcStr, String... compareStr)
272    { return XOR("equalsXOR", srcStr, compareStr, cmp -> srcStr.equals(cmp)); }
273
274    /**
275     * <EMBED CLASS=defs DATA-DESC='does not equal any' DATA-CI='is'>
276     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC>
277     * @param srcStr Any non-null instance of a {@code java.lang.String}
278     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
279     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET>
280     * @see #NAND(String, String, String[], Predicate)
281     */
282    public static boolean equalsNAND(String srcStr, String... compareStr)
283    { return NAND("equalsNAND", srcStr, compareStr, cmp -> srcStr.equals(cmp)); }
284
285    /**
286     * <EMBED CLASS=defs DATA-DESC='equals exactly one' DATA-CI='is not'>
287     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC>
288     * @param srcStr Any non-null instance of a {@code java.lang.String}
289     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
290     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET>
291     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCXORNOTE>
292     * @see #XOR(String, String, String[], Predicate)
293     */
294    public static boolean equalsXOR_CI(String srcStr, String... compareStr)
295    { return XOR("equalsXOR_CI", srcStr, compareStr, cmp -> srcStr.equalsIgnoreCase(cmp)); }
296
297    /**
298     * <EMBED CLASS=defs DATA-DESC='does not equal any' DATA-CI='is not'>
299     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC>
300     * @param srcStr Any non-null instance of a {@code java.lang.String}
301     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
302     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET>
303     * @see #NAND(String, String, String[], Predicate)
304     */
305    public static boolean equalsNAND_CI(String srcStr, String... compareStr)
306    { return NAND("equalsNAND_CI", srcStr, compareStr, cmp -> srcStr.equalsIgnoreCase(cmp)); }
307
308
309    // ********************************************************************************************
310    // ********************************************************************************************
311    // CONTAINS
312    // ********************************************************************************************
313    // ********************************************************************************************
314
315
316    /**
317     * <EMBED CLASS=defs DATA-DESC='contains at least one' DATA-CI='is'>
318     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC>
319     * @param srcStr Any non-null instance of a {@code java.lang.String}
320     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
321     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET>
322     * @see #CONTAINS(boolean, byte, String, String[])
323     */
324    public static boolean containsOR(String srcStr, String... compareStr)
325    { return CONTAINS(false, OR, srcStr, compareStr); }
326
327    /**
328     * <EMBED CLASS=defs DATA-DESC='contains every one' DATA-CI='is'>
329     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC>
330     * @param srcStr Any non-null instance of a {@code java.lang.String}
331     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
332     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET>
333     * @see #CONTAINS(boolean, byte, String, String[])
334     */
335    public static boolean containsAND(String srcStr, String... compareStr)
336    { return CONTAINS(false, AND, srcStr, compareStr); }
337
338    /**
339     * <EMBED CLASS=defs DATA-DESC='contains exactly one' DATA-CI='is'>
340     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC>
341     * @param srcStr Any non-null instance of a {@code java.lang.String}
342     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
343     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET>
344     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCXORNOTE>
345     * @see #CONTAINS(boolean, byte, String, String[])
346     */
347    public static boolean containsXOR(String srcStr, String... compareStr)
348    { return CONTAINS(false, XOR, srcStr, compareStr); }
349
350    /**
351     * <EMBED CLASS=defs DATA-DESC='does not contain any' DATA-CI='is'>
352     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC>
353     * @param srcStr Any non-null instance of a {@code java.lang.String}
354     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
355     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET>
356     * @see #CONTAINS(boolean, byte, String, String[])
357     */
358    public static boolean containsNAND(String srcStr, String... compareStr)
359    { return CONTAINS(false, NAND, srcStr, compareStr); }
360
361    /**
362     * <EMBED CLASS=defs DATA-DESC='contains at least one' DATA-CI='is not'>
363     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC>
364     * @param srcStr Any non-null instance of a {@code java.lang.String}
365     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
366     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET>
367     * @see #CONTAINS(boolean, byte, String, String[])
368     */
369    public static boolean containsOR_CI(String srcStr, String... compareStr)
370    { return CONTAINS(true, OR, srcStr, compareStr); }
371
372    /**
373     * <EMBED CLASS=defs DATA-DESC='contains every one' DATA-CI='is not'>
374     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC>
375     * @param srcStr Any non-null instance of a {@code java.lang.String}
376     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
377     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET>
378     * @see #CONTAINS(boolean, byte, String, String[])
379     */
380    public static boolean containsAND_CI(String srcStr, String... compareStr)
381    { return CONTAINS(true, AND, srcStr, compareStr); }
382
383    /**
384     * <EMBED CLASS=defs DATA-DESC='contains exactly one' DATA-CI='is not'>
385     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC>
386     * @param srcStr Any non-null instance of a {@code java.lang.String}
387     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
388     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET>
389     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCXORNOTE>
390     * @see #CONTAINS(boolean, byte, String, String[])
391     */
392    public static boolean containsXOR_CI(String srcStr, String... compareStr)
393    { return CONTAINS(true, XOR, srcStr, compareStr); }
394
395    /**
396     * <EMBED CLASS=defs DATA-DESC='does not contain any' DATA-CI='is not'>
397     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC>
398     * @param srcStr Any non-null instance of a {@code java.lang.String}
399     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
400     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET>
401     * @see #CONTAINS(boolean, byte, String, String[])
402     */
403    public static boolean containsNAND_CI(String srcStr, String... compareStr)
404    { return CONTAINS(true, NAND, srcStr, compareStr); }
405
406
407    // ********************************************************************************************
408    // ********************************************************************************************
409    // STARTS-WITH, ENDS-WITH
410    // ********************************************************************************************
411    // ********************************************************************************************
412
413
414    /**
415     * <EMBED CLASS=defs DATA-DESC='ends with exactly one' DATA-CI='is'>
416     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC>
417     * @param srcStr Any non-null instance of a {@code java.lang.String}
418     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
419     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET>
420     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCXORNOTE>
421     * @see #XOR(String, String, String[], Predicate)
422     */
423    public static boolean endsWithXOR(String srcStr, String... compareStr)
424    { return XOR("endsWithXOR", srcStr, compareStr, cmp -> srcStr.endsWith(cmp)); }
425
426    /**
427     * <EMBED CLASS=defs DATA-DESC='does not end with any' DATA-CI='is'>
428     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC>
429     * @param srcStr Any non-null instance of a {@code java.lang.String}
430     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
431     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET>
432     * @see #NAND(String, String, String[], Predicate)
433     */
434    public static boolean endsWithNAND(String srcStr, String... compareStr)
435    { return NAND("endsWithNAND", srcStr, compareStr, cmp -> srcStr.endsWith(cmp)); }
436
437    /**
438     * <EMBED CLASS=defs DATA-DESC='starts with exactly one' DATA-CI='is'>
439     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC>
440     * @param srcStr Any non-null instance of a {@code java.lang.String}
441     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
442     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET>
443     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCXORNOTE>
444     * @see #XOR(String, String, String[], Predicate)
445     */
446    public static boolean startsWithXOR(String srcStr, String ... compareStr)
447    { return XOR("startsWithXOR", srcStr, compareStr, cmp -> srcStr.startsWith(cmp)); }
448
449    /**
450     * <EMBED CLASS=defs DATA-DESC='does not start with any' DATA-CI='is'>
451     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC>
452     * @param srcStr Any non-null instance of a {@code java.lang.String}
453     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
454     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET>
455     * @see #NAND(String, String, String[], Predicate)
456     */
457    public static boolean startsWithNAND(String srcStr, String ... compareStr)
458    { return NAND("startsWithNAND", srcStr, compareStr, cmp -> srcStr.startsWith(cmp)); }
459
460
461    // ********************************************************************************************
462    // ********************************************************************************************
463    // STARTS-WITH, ENDS-WITH - CASE INSENSITIVE
464    // ********************************************************************************************
465    // ********************************************************************************************
466
467
468    /**
469     * <EMBED CLASS=defs DATA-DESC='ends with exactly one' DATA-CI='is not'>
470     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC>
471     * @param srcStr Any non-null instance of a {@code java.lang.String}
472     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
473     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET>
474     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCXORNOTE>
475     * @see #endsWithIgnoreCase(String, String)
476     * @see #XOR(String, String, String[], Predicate)
477     */
478    public static boolean endsWithXOR_CI(String srcStr, String... compareStr)
479    { return XOR("endsWithXOR_CI", srcStr, compareStr, cmp -> endsWithIgnoreCase(srcStr, cmp)); }
480
481    /**
482     * <EMBED CLASS=defs DATA-DESC='does not end with any' DATA-CI='is not'>
483     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC>
484     * @param srcStr Any non-null instance of a {@code java.lang.String}
485     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
486     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET>
487     * @see #endsWithIgnoreCase(String, String)
488     * @see #NAND(String, String, String[], Predicate)
489     */
490    public static boolean endsWithNAND_CI(String srcStr, String... compareStr)
491    { return NAND("endsWithNAND_CI", srcStr, compareStr, cmp -> endsWithIgnoreCase(srcStr, cmp)); }
492
493    /**
494     * <EMBED CLASS=defs DATA-DESC='starts with exactly one' DATA-CI='is not'>
495     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC>
496     * @param srcStr Any non-null instance of a {@code java.lang.String}
497     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
498     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET>
499     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCXORNOTE>
500     * @see #startsWithIgnoreCase(String, String)
501     * @see #XOR(String, String, String[], Predicate)
502     */
503    public static boolean startsWithXOR_CI(String srcStr, String ... compareStr)
504    { return XOR("startsWithXOR_CI", srcStr, compareStr, cmp -> startsWithIgnoreCase(srcStr, cmp)); }
505
506    /**
507     * <EMBED CLASS=defs DATA-DESC='does not start with any' DATA-CI='is not'>
508     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC>
509     * @param srcStr Any non-null instance of a {@code java.lang.String}
510     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
511     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET>
512     * @see #startsWithIgnoreCase(String, String)
513     * @see #NAND(String, String, String[], Predicate)
514     */
515    public static boolean startsWithNAND_CI(String srcStr, String ... compareStr)
516    { return NAND("startsWithNAND_CI", srcStr, compareStr, cmp -> startsWithIgnoreCase(srcStr, cmp)); }
517
518
519    // ********************************************************************************************
520    // ********************************************************************************************
521    // IGNORE-CASE METHODS
522    // ********************************************************************************************
523    // ********************************************************************************************
524
525
526    /**
527     * This performs the exact same comparison as Java's {@code String.startsWith(String)} method.
528     * Java provides an {@code 'equalsIgnoreCase()'} method, but not an
529     * {@code 'startsWithIgnoreCase()'}.  This method does just that.
530     * @param srcStr This {@code String} is checked to see if it starts with the {@code compareStr}.
531     * @param compareStr This {@code String} is checked against the {@code srcStr} - specifically,
532     * if {@code srcStr} ends with {@code compareStr}
533     * @return {@code TRUE} if {@code srcStr} starts with {@code compareStr} (ignoring-case), and
534     * {@code FALSE} otherwise.
535     */
536    public static boolean startsWithIgnoreCase(String srcStr, String compareStr)
537    { return srcStr.regionMatches(true, 0, compareStr, 0, compareStr.length()); }
538
539    /**
540     * This performs the exact same comparison as Java's {@code String.endsWith(String)} method.
541     * Java provides an {@code 'equalsIgnoreCase()'} method, but not an
542     * {@code 'endsWithIgnoreCase()'}.  This method does just that.
543     * @param srcStr This {@code String} is checked to see if it ends with the {@code compareStr}.
544     * @param compareStr This {@code String} is checked against the {@code srcStr} - specifically,
545     * if {@code srcStr} ends with {@code compareStr}
546     * @return {@code TRUE} if {@code srcStr} ends with {@code compareStr} (ignoring-case),
547     * and {@code FALSE} otherwise.
548     */
549    public static boolean endsWithIgnoreCase(String srcStr, String compareStr)
550    {
551        int compareStrLen   = compareStr.length();
552        int srcStrLen       = srcStr.length();
553
554        return srcStr.regionMatches(true, srcStrLen - compareStrLen, compareStr, 0, compareStrLen);
555    }
556
557    /**
558     * This performs the exact same comparison as Java's {@code String.contains(String)} method.
559     * Java provides an {@code 'equalsIgnoreCase()'} method, but not a
560     * {@code 'containsIgnoreCase()'}.  This method does just that.
561     * @param srcStr This {@code String} is checked to see if it contains the {@code compareStr}
562     * @param compareStr This {@code String} is checked against the {@code srcStr} - specifically,
563     * if {@code compareStr} is contained by {@code srcStr}
564     * @return {@code TRUE} if {@code compareStr} is a substring of {@code srcStr} (ignoring-case),
565     * and {@code FALSE} otherwise.
566     * @see #containsIgnoreCase(String, LV, String)
567     */
568    public static boolean containsIgnoreCase(String srcStr, String compareStr)
569    { return containsIgnoreCase(srcStr, new LV(srcStr, 0, srcStr.length()), compareStr); }
570
571
572
573    // ********************************************************************************************
574    // ********************************************************************************************
575    // ********************************************************************************************
576    // ********************************************************************************************
577    // ********************************************************************************************
578    // StrCmpr Main Section #2
579    // ********************************************************************************************
580    // ********************************************************************************************
581    // ********************************************************************************************
582    // ********************************************************************************************
583    // ********************************************************************************************
584
585
586
587    // ********************************************************************************************
588    // ********************************************************************************************
589    // EQUALS
590    // ********************************************************************************************
591    // ********************************************************************************************
592
593
594    /**
595     * <EMBED CLASS=defs DATA-DESC='equals exactly one' DATA-CI='is'>
596     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_SE>
597     * @param srcStr Any non-null instance of a {@code java.lang.String}
598     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
599     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
600     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
601     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_SE>
602     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCXORNOTE>
603     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
604     * @see #equals(String, LV, String)
605     * @see #XOR(String, String, String[], Predicate)
606     */
607    public static boolean equalsXOR(String srcStr, int sPos, int ePos, String... compareStr)
608    {
609        LV l = new LV(srcStr, sPos, ePos);
610        return XOR("equalsXOR", srcStr, compareStr, cmp -> equals(srcStr, l, cmp));
611    }
612
613    /**
614     * <EMBED CLASS=defs DATA-DESC='does not equal any' DATA-CI='is'>
615     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_SE>
616     * @param srcStr Any non-null instance of a {@code java.lang.String}
617     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
618     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
619     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
620     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_SE>
621     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
622     * @see #equals(String, LV, String)
623     * @see #NAND(String, String, String[], Predicate)
624     */
625    public static boolean equalsNAND(String srcStr, int sPos, int ePos, String... compareStr)
626    {
627        LV l = new LV(srcStr, sPos, ePos);
628        return NAND("equalsNAND", srcStr, compareStr, cmp -> equals(srcStr, l, cmp));
629    }
630
631    /**
632     * <EMBED CLASS=defs DATA-DESC='equals exactly one' DATA-CI='is not'>
633     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_SE>
634     * @param srcStr Any non-null instance of a {@code java.lang.String}
635     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
636     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
637     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
638     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_SE>
639     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCXORNOTE>
640     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
641     * @see #equalsIgnoreCase(String, LV, String)
642     * @see #XOR(String, String, String[], Predicate)
643     */
644    public static boolean equalsXOR_CI(String srcStr, int sPos, int ePos, String... compareStr)
645    {
646        LV l = new LV(srcStr, sPos, ePos);
647        return XOR("equalsXOR_CI", srcStr, compareStr, cmp -> equalsIgnoreCase(srcStr, l, cmp));
648    }
649
650    /**
651     * <EMBED CLASS=defs DATA-DESC='does not equal any' DATA-CI='is not'>
652     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_SE>
653     * @param srcStr Any non-null instance of a {@code java.lang.String}
654     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
655     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
656     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
657     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_SE>
658     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
659     * @see #equalsIgnoreCase(String, LV, String)
660     * @see #NAND(String, String, String[], Predicate)
661     */
662    public static boolean equalsNAND_CI(String srcStr, int sPos, int ePos, String... compareStr)
663    {
664        LV l = new LV(srcStr, sPos, ePos);
665        return NAND("equalsNAND_CI", srcStr, compareStr, cmp -> equalsIgnoreCase(srcStr, l, cmp));
666    }
667
668
669    // ********************************************************************************************
670    // ********************************************************************************************
671    // CONTAINS - This code is redundant
672    // ********************************************************************************************
673    // ********************************************************************************************
674
675
676    /**
677     * <EMBED CLASS=defs DATA-DESC='contains at least one' DATA-CI='is'>
678     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_SE>
679     * @param srcStr Any non-null instance of a {@code java.lang.String}
680     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
681     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
682     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
683     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_SE>
684     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
685     * @see #CONTAINS(boolean, byte, LV, String, String[])
686     */
687    public static boolean containsOR(String srcStr, int sPos, int ePos, String... compareStr)
688    {
689        LV l = new LV(srcStr, sPos, ePos);
690        return CONTAINS(false, OR, l, srcStr, compareStr);
691    }
692
693    /**
694     * <EMBED CLASS=defs DATA-DESC='contains every one' DATA-CI='is'>
695     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_SE>
696     * @param srcStr Any non-null instance of a {@code java.lang.String}
697     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
698     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
699     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
700     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_SE>
701     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
702     * @see #CONTAINS(boolean, byte, LV, String, String[])
703     */
704    public static boolean containsAND(String srcStr, int sPos, int ePos, String... compareStr)
705    {
706        LV l = new LV(srcStr, sPos, ePos);
707        return CONTAINS(false, AND, l, srcStr, compareStr);
708    }
709
710    /**
711     * <EMBED CLASS=defs DATA-DESC='contains exactly one' DATA-CI='is'>
712     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_SE>
713     * @param srcStr Any non-null instance of a {@code java.lang.String}
714     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
715     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
716     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
717     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_SE>
718     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCXORNOTE>
719     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
720     * @see #CONTAINS(boolean, byte, LV, String, String[])
721     */
722    public static boolean containsXOR(String srcStr, int sPos, int ePos, String... compareStr)
723    {
724        LV l = new LV(srcStr, sPos, ePos);
725        return CONTAINS(false, XOR, l, srcStr, compareStr);
726    }
727
728    /**
729     * <EMBED CLASS=defs DATA-DESC='does not contain any' DATA-CI='is'>
730     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_SE>
731     * @param srcStr Any non-null instance of a {@code java.lang.String}
732     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
733     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
734     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
735     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_SE>
736     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
737     * @see #CONTAINS(boolean, byte, LV, String, String[])
738     */
739    public static boolean containsNAND(String srcStr, int sPos, int ePos, String... compareStr)
740    {
741        LV l = new LV(srcStr, sPos, ePos);
742        return CONTAINS(false, NAND, l, srcStr, compareStr);
743    }
744
745    /**
746     * <EMBED CLASS=defs DATA-DESC='contains at least one' DATA-CI='is not'>
747     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_SE>
748     * @param srcStr Any non-null instance of a {@code java.lang.String}
749     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
750     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
751     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
752     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_SE>
753     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
754     * @see #CONTAINS(boolean, byte, LV, String, String[])
755     */
756    public static boolean containsOR_CI(String srcStr, int sPos, int ePos, String... compareStr)
757    {
758        LV l = new LV(srcStr, sPos, ePos);
759        return CONTAINS(true, OR, l, srcStr, compareStr);
760    }
761
762    /**
763     * <EMBED CLASS=defs DATA-DESC='contains every one' DATA-CI='is not'>
764     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_SE>
765     * @param srcStr Any non-null instance of a {@code java.lang.String}
766     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
767     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
768     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
769     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_SE>
770     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
771     * @see #CONTAINS(boolean, byte, LV, String, String[])
772     */
773    public static boolean containsAND_CI(String srcStr, int sPos, int ePos, String... compareStr)
774    {
775        LV l = new LV(srcStr, sPos, ePos);
776        return CONTAINS(true, AND, l, srcStr, compareStr);
777    }
778
779    /**
780     * <EMBED CLASS=defs DATA-DESC='contains exactly one' DATA-CI='is not'>
781     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_SE>
782     * @param srcStr Any non-null instance of a {@code java.lang.String}
783     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
784     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
785     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
786     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_SE>
787     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCXORNOTE>
788     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
789     * @see #CONTAINS(boolean, byte, LV, String, String[])
790     */
791    public static boolean containsXOR_CI(String srcStr, int sPos, int ePos, String... compareStr)
792    {
793        LV l = new LV(srcStr, sPos, ePos);
794        return CONTAINS(true, XOR, l, srcStr, compareStr);
795    }
796
797    /**
798     * <EMBED CLASS=defs DATA-DESC='does not contain any' DATA-CI='is not'>
799     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_SE>
800     * @param srcStr Any non-null instance of a {@code java.lang.String}
801     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
802     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
803     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
804     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_SE>
805     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
806     * @see #CONTAINS(boolean, byte, LV, String, String[])
807     */
808    public static boolean containsNAND_CI(String srcStr, int sPos, int ePos, String... compareStr)
809    {
810        LV l = new LV(srcStr, sPos, ePos);
811        return CONTAINS(true, NAND, l, srcStr, compareStr);
812    }
813
814
815    // ********************************************************************************************
816    // ********************************************************************************************
817    // STARTS-WITH, ENDS-WITH
818    // ********************************************************************************************
819    // ********************************************************************************************
820
821
822    /**
823     * <EMBED CLASS=defs DATA-DESC='ends with exactly one' DATA-CI='is'>
824     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_SE>
825     * @param srcStr Any non-null instance of a {@code java.lang.String}
826     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
827     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
828     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
829     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_SE>
830     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCXORNOTE>
831     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
832     * @see #endsWith(String, LV, String)
833     * @see #XOR(String, String, String[], Predicate)
834     */
835    public static boolean endsWithXOR(String srcStr, int sPos, int ePos, String... compareStr)
836    {
837        LV l = new LV(srcStr, sPos, ePos);
838        return XOR("endsWithXOR", srcStr, compareStr, cmp -> endsWith(srcStr, l, cmp));
839    }
840
841    /**
842     * <EMBED CLASS=defs DATA-DESC='does not end with any' DATA-CI='is'>
843     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_SE>
844     * @param srcStr Any non-null instance of a {@code java.lang.String}
845     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
846     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
847     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
848     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_SE>
849     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
850     * @see #endsWith(String, LV, String)
851     * @see #NAND(String, String, String[], Predicate)
852     */
853    public static boolean endsWithNAND(String srcStr, int sPos, int ePos, String... compareStr)
854    {
855        LV l = new LV(srcStr, sPos, ePos);
856        return NAND("endsWithNAND", srcStr, compareStr, cmp -> endsWith(srcStr, l, cmp));
857    }
858
859    /**
860     * <EMBED CLASS=defs DATA-DESC='starts with exactly one' DATA-CI='is'>
861     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_SE>
862     * @param srcStr Any non-null instance of a {@code java.lang.String}
863     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
864     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
865     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
866     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_SE>
867     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCXORNOTE>
868     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
869     * @see #startsWith(String, LV, String)
870     * @see #XOR(String, String, String[], Predicate)
871     */
872    public static boolean startsWithXOR(String srcStr, int sPos, int ePos, String ... compareStr)
873    {
874        LV l = new LV(srcStr, sPos, ePos);
875        return XOR("startsWithXOR", srcStr, compareStr, cmp -> startsWith(srcStr, l, cmp));
876    }
877
878    /**
879     * <EMBED CLASS=defs DATA-DESC='does not start with any' DATA-CI='is'>
880     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_SE>
881     * @param srcStr Any non-null instance of a {@code java.lang.String}
882     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
883     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
884     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
885     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_SE>
886     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
887     * @see #startsWith(String, LV, String)
888     * @see #NAND(String, String, String[], Predicate)
889     */
890    public static boolean startsWithNAND(String srcStr, int sPos, int ePos, String ... compareStr)
891    {
892        LV l = new LV(srcStr, sPos, ePos);
893        return NAND("startsWithNAND", srcStr, compareStr, cmp -> startsWith(srcStr, l, cmp));
894    }
895
896
897    // ********************************************************************************************
898    // ********************************************************************************************
899    // STARTS-WITH, ENDS-WITH - CASE INSENSITIVE
900    // ********************************************************************************************
901    // ********************************************************************************************
902
903
904    /**
905     * <EMBED CLASS=defs DATA-DESC='ends with exactly one' DATA-CI='is not'>
906     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_SE>
907     * @param srcStr Any non-null instance of a {@code java.lang.String}
908     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
909     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
910     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
911     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_SE>
912     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCXORNOTE>
913     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
914     * @see #endsWithIgnoreCase(String, LV, String)
915     * @see #XOR(String, String, String[], Predicate)
916     */
917    public static boolean endsWithXOR_CI(String srcStr, int sPos, int ePos, String... compareStr)
918    {
919        LV l = new LV(srcStr, sPos, ePos);
920        return XOR("endsWithXOR_CI", srcStr, compareStr, cmp -> endsWithIgnoreCase(srcStr, l, cmp));
921    }
922
923    /**
924     * <EMBED CLASS=defs DATA-DESC='does not end with any' DATA-CI='is not'>
925     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_SE>
926     * @param srcStr Any non-null instance of a {@code java.lang.String}
927     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
928     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
929     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
930     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_SE>
931     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
932     * @see #endsWithIgnoreCase(String, LV, String)
933     * @see #NAND(String, String, String[], Predicate)
934     */
935    public static boolean endsWithNAND_CI(String srcStr, int sPos, int ePos, String... compareStr)
936    {
937        LV l = new LV(srcStr, sPos, ePos);
938        return NAND("endsWithNAND_CI", srcStr, compareStr, cmp -> endsWithIgnoreCase(srcStr, l, cmp));
939    }
940
941    /**
942     * <EMBED CLASS=defs DATA-DESC='starts with exactly one' DATA-CI='is not'>
943     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_SE>
944     * @param srcStr Any non-null instance of a {@code java.lang.String}
945     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
946     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
947     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
948     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_SE>
949     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCXORNOTE>
950     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
951     * @see #startsWithIgnoreCase(String, LV, String)
952     * @see #XOR(String, String, String[], Predicate)
953     */
954    public static boolean startsWithXOR_CI(String srcStr, int sPos, int ePos, String ... compareStr)
955    {
956        LV l = new LV(srcStr, sPos, ePos);
957        return XOR("startsWithXOR_CI", srcStr, compareStr, cmp -> startsWithIgnoreCase(srcStr, l, cmp));
958    }
959
960    /**
961     * <EMBED CLASS=defs DATA-DESC='does not start with any' DATA-CI='is not'>
962     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_SE>
963     * @param srcStr Any non-null instance of a {@code java.lang.String}
964     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
965     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
966     * @param compareStr The {@code String's} used in the comparison against {@code 'srcStr'}
967     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_SE>
968     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
969     * @see #startsWithIgnoreCase(String, LV, String)
970     * @see #NAND(String, String, String[], Predicate)
971     */
972    public static boolean startsWithNAND_CI(String srcStr, int sPos, int ePos, String ... compareStr)
973    {
974        LV l = new LV(srcStr, sPos, ePos);
975        return NAND("startsWithNAND_CI", srcStr, compareStr, cmp -> startsWithIgnoreCase(srcStr, l, cmp));
976    }
977
978
979    // ********************************************************************************************
980    // ********************************************************************************************
981    //  Single String Methods
982    // ********************************************************************************************
983    // ********************************************************************************************
984
985
986    /**
987     * <EMBED CLASS=defs DATA-FUNC='equals' DATA-CI='is'>
988     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_1C>
989     * @param srcStr Any non-null instance of a {@code java.lang.String}
990     * @param compareStr The {@code String} used in the comparison against {@code 'srcStr'}
991     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
992     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
993     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_1C>
994     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
995     * @see #equals(String, LV, String)
996     */
997    public static boolean equals(String srcStr, int sPos, int ePos, String compareStr)
998    { return equals(srcStr, new LV(srcStr, sPos, ePos), compareStr); }
999
1000
1001    /**
1002     * <EMBED CLASS=defs DATA-FUNC='equals' DATA-CI='is not'>
1003     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_1C>
1004     * @param srcStr Any non-null instance of a {@code java.lang.String}
1005     * @param compareStr The {@code String} used in the comparison against {@code 'srcStr'}
1006     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
1007     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
1008     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_1C>
1009     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
1010     * @see #equalsIgnoreCase(String, LV, String)
1011     */
1012    public static boolean equalsIgnoreCase(String srcStr, int sPos, int ePos, String compareStr)
1013    { return equalsIgnoreCase(srcStr, new LV(srcStr, sPos, ePos), compareStr); }
1014
1015
1016    /**
1017     * <EMBED CLASS=defs DATA-FUNC='starts with' DATA-CI='is'>
1018     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_1C>
1019     * @param srcStr Any non-null instance of a {@code java.lang.String}
1020     * @param compareStr The {@code String} used in the comparison against {@code 'srcStr'}
1021     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
1022     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
1023     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_1C>
1024     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
1025     * @see #startsWith(String, LV, String)
1026     */
1027    public static boolean startsWith(String srcStr, int sPos, int ePos, String compareStr)
1028    { return startsWith(srcStr, new LV(srcStr, sPos, ePos), compareStr); }
1029
1030    /**
1031     * <EMBED CLASS=defs DATA-FUNC='starts with' DATA-CI='is not'>
1032     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_1C>
1033     * @param srcStr Any non-null instance of a {@code java.lang.String}
1034     * @param compareStr The {@code String} used in the comparison against {@code 'srcStr'}
1035     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
1036     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
1037     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_1C>
1038     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
1039     * @see #startsWithIgnoreCase(String, LV, String)
1040     */
1041    public static boolean startsWithIgnoreCase(String srcStr, int sPos, int ePos, String compareStr)
1042    { return startsWithIgnoreCase(srcStr, new LV(srcStr, sPos, ePos), compareStr); }
1043
1044    /**
1045     * <EMBED CLASS=defs DATA-FUNC='ends with' DATA-CI='is'>
1046     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_1C>
1047     * @param srcStr Any non-null instance of a {@code java.lang.String}
1048     * @param compareStr The {@code String} used in the comparison against {@code 'srcStr'}
1049     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
1050     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
1051     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_1C>
1052     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
1053     * @see #endsWith(String, LV, String)
1054     */
1055    public static boolean endsWith(String srcStr, int sPos, int ePos, String compareStr)
1056    { return endsWith(srcStr, new LV(srcStr, sPos, ePos), compareStr); }
1057
1058    /**
1059     * <EMBED CLASS=defs DATA-FUNC='ends with' DATA-CI='is not'>
1060     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_1C>
1061     * @param srcStr Any non-null instance of a {@code java.lang.String}
1062     * @param compareStr The {@code String} used in the comparison against {@code 'srcStr'}
1063     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
1064     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
1065     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_1C>
1066     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
1067     * @see #endsWithIgnoreCase(String, LV, String)
1068     */
1069    public static boolean endsWithIgnoreCase(String srcStr, int sPos, int ePos, String compareStr)
1070    { return endsWithIgnoreCase(srcStr, new LV(srcStr, sPos, ePos), compareStr); }
1071
1072
1073    /**
1074     * <EMBED CLASS=defs DATA-FUNC='contains' DATA-CI='is'>
1075     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_1C>
1076     * @param srcStr Any non-null instance of a {@code java.lang.String}
1077     * @param compareStr The {@code String} used in the comparison against {@code 'srcStr'}
1078     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
1079     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
1080     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_1C>
1081     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
1082     * @see #contains(String, LV, String)
1083     */
1084    public static boolean contains(String srcStr, int sPos, int ePos, String compareStr)
1085    { return contains(srcStr, new LV(srcStr, sPos, ePos), compareStr); }
1086
1087
1088    /**
1089     * <EMBED CLASS=defs DATA-FUNC='contains' DATA-CI='is not'>
1090     * <EMBED CLASS='external-html' DATA-FILE-ID=STRCDESC_1C>
1091     * @param srcStr Any non-null instance of a {@code java.lang.String}
1092     * @param compareStr The {@code String} used in the comparison against {@code 'srcStr'}
1093     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSSTR>
1094     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSSTR>
1095     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRCRET_1C>
1096     * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX>
1097     * @see #containsIgnoreCase(String, LV, String)
1098     */
1099    public static boolean containsIgnoreCase(String srcStr, int sPos, int ePos, String compareStr)
1100    { return containsIgnoreCase(srcStr, new LV(srcStr, sPos, ePos), compareStr); }
1101
1102
1103    // ********************************************************************************************
1104    // ********************************************************************************************
1105    // PROTECTED METHODS
1106    // ********************************************************************************************
1107    // ********************************************************************************************
1108
1109
1110    /**
1111     * This method remains {@code 'protected'} because it utilizes a specialized Loop-Control
1112     * Variable Class Version of {@code 'LV'}.  It is used internally by the search methods above.
1113     */
1114    protected static boolean equals(String srcStr, LV l, String compareStr)
1115    {
1116        return ((l.end - l.start) == compareStr.length())
1117                && srcStr.regionMatches(l.start, compareStr, 0, compareStr.length());
1118    }
1119
1120    /**
1121     * This method remains {@code 'protected'} because it utilizes a specialized Loop-Control 
1122     * Variable Class Version of {@code 'LV'}.  It is used internally by the search methods above.
1123     */
1124    protected static boolean equalsIgnoreCase(String srcStr, LV l, String compareStr)
1125    {
1126        return ((l.end - l.start) == compareStr.length())
1127                && srcStr.regionMatches(true, l.start, compareStr, 0, compareStr.length());
1128    }
1129
1130    /**
1131     * This method remains {@code 'protected'} because it utilizes a specialized Loop-Control
1132     * Variable Class Version of {@code 'LV'}.  It is used internally by the search methods above.
1133     */
1134    protected static boolean startsWith(String srcStr, LV l, String compareStr)
1135    {
1136        return ((l.end - l.start) >= compareStr.length()) 
1137                && srcStr.regionMatches(l.start, compareStr, 0, compareStr.length());
1138    }
1139
1140    /**
1141     * This method remains {@code 'protected'} because it utilizes a specialized Loop-Control
1142     * Variable Class Version of {@code 'LV'}.  It is used internally by the search methods above.
1143     */
1144    protected static boolean startsWithIgnoreCase(String srcStr, LV l, String compareStr)
1145    {
1146        return ((l.end - l.start) >= compareStr.length()) 
1147                && srcStr.regionMatches(true, l.start, compareStr, 0, compareStr.length());
1148    }
1149
1150    /**
1151     * This method remains {@code 'protected'} because it utilizes a specialized Loop-Control
1152     * Variable Class Version of {@code 'LV'}.  It is used internally by the search methods above.
1153     */
1154    protected static boolean endsWith(String srcStr, LV l, String compareStr)
1155    {
1156        int compareStrLen = compareStr.length();
1157
1158        return ((l.end - l.start) >= compareStrLen)
1159                && srcStr.regionMatches(l.end - compareStrLen, compareStr, 0, compareStrLen);
1160    }
1161
1162    /**
1163     * This method remains {@code 'protected'} because it utilizes a specialized Loop-Control
1164     * Variable Class Version of {@code 'LV'}.  It is used internally by the search methods above.
1165     */
1166    protected static boolean endsWithIgnoreCase(String srcStr, LV l, String compareStr)
1167    {
1168        int compareStrLen = compareStr.length();
1169    
1170        return ((l.end - l.start) >= compareStrLen)
1171                && srcStr.regionMatches(true, l.end - compareStrLen, compareStr, 0, compareStrLen);
1172    }
1173
1174    /**
1175     * This method remains {@code 'protected'} because it utilizes a specialized Loop-Control
1176     * Variable Class Version of {@code 'LV'}.  It is used internally by the search methods above.
1177     */
1178    protected static boolean contains(String srcStr, LV l, String compareStr)
1179    {
1180        // String.regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len)
1181        // Tests if two string regions are equal.  A substring of this {@code String} object is
1182        // compared to a substring of the argument other.  The result is true if these substrings
1183        // represent character sequences that are the same, ignoring case if and only if ignoreCase
1184        // is true.
1185
1186        int compareStrLen   = compareStr.length();
1187        int loopLen         = l.end - compareStrLen;
1188
1189        if ((l.end - l.start) < compareStrLen) return false;
1190
1191        for (int i=l.start; i <= loopLen; i++)
1192            if (srcStr.regionMatches(i, compareStr, 0, compareStrLen))
1193                return true;
1194
1195        return false;
1196    }
1197
1198    /**
1199     * This method remains {@code 'protected'} because it utilizes a specialized Loop-Control
1200     * Variable Class Version of {@code 'LV'}.  It is used internally by the search methods above.
1201     */
1202    protected static boolean containsIgnoreCase(String srcStr, LV l, String compareStr)
1203    {
1204        // String.regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len)
1205        // Tests if two string regions are equal.  A substring of this {@code String} object is
1206        // compared to a substring of the argument other.  The result is true if these substrings
1207        // represent character sequences that are the same, ignoring case if and only if ignoreCase
1208        // is true.
1209
1210        int compareStrLen   = compareStr.length();
1211        int loopLen         = l.end - compareStrLen;
1212
1213        if ((l.end - l.start) < compareStrLen) return false;
1214
1215        for (int i=l.start; i <= loopLen; i++)
1216            if (srcStr.regionMatches(true, i, compareStr, 0, compareStrLen))
1217                return true;
1218
1219        return false;
1220    }
1221
1222
1223    // ********************************************************************************************
1224    // ********************************************************************************************
1225    // CONTAINS OPTIMIZATION
1226    // ********************************************************************************************
1227    // ********************************************************************************************
1228
1229
1230    /**
1231     * Signifies that an {@code 'AND'} operation is required, but only for methods that implement
1232     * one of the {@code 'contains'} variants.
1233     */
1234    protected static final byte AND = 0;
1235
1236    /**
1237     * Signifies that an {@code 'AND'} operation is required, but only for methods that implement
1238     * one of the {@code 'contains'} variants.
1239     */
1240    protected static final byte OR = 1;
1241
1242    /**
1243     * Signifies that an {@code 'AND'} operation is required, but only for methods that implement
1244     * one of the {@code 'contains'} variants.
1245     */
1246    protected static final byte NAND = 2;
1247
1248    /**
1249     * Signifies that an {@code 'AND'} operation is required, but only for methods that implement
1250     * one of the {@code 'contains'} variants.
1251     */
1252    protected static final byte XOR = 3;
1253
1254    // This is sort-of overkill.  It is better to error in favor of caution, than to pretend.
1255    // I guess... :)
1256    private static final String CONTAINS_EX = 
1257        "The value passed to parameter 'booleanOperation' was [BOOL]." +
1258        "However, this parameter is intended to must be chosen from one of the four boolean " +
1259        "static-constants available in this class.  Valid values for this parameter include: " +
1260        "StrCmpr.AND, StrCmpr.OR, StrCmpr.XOR and StrCmpr.NAND.";
1261
1262    /**
1263     * Implementing a 'contains' is more efficient by performing only one loop iteration.
1264     * Therefore, the methods that implement some variant of the {@code String.contains} method
1265     * will use this general-purpose {@code contains}-testing method.
1266     * @param ignoresCase This is used to signify whether to ignore case when performing the
1267     * {@code String}-comparison operations.
1268     * @param booleanOperation This is designed to take one of four values.  This method will
1269     * throw an exception if it does not.
1270     * @param l Loop end-point parameter
1271     * @param srcStr Any non-null instance of {@code java.lang.String}
1272     * @param cmpStrs This is one (or many) java {@code String's}, each of which shall be
1273     * compared to the ending of {@code 'srcStr'}.
1274     * @return The appropriate response based on the inputs for boolean logic.
1275     */
1276    protected static boolean CONTAINS(
1277        boolean ignoresCase, byte booleanOperation, LV l, String srcStr, String[] cmpStrs
1278    )
1279    {
1280        // Check each of the compare-strs first.  It will provide a better exception message
1281        for (String s : cmpStrs)
1282            if (s == null) throw new NullPointerException
1283                ("One of the elements of 'compareStr' was null");
1284
1285        if ((booleanOperation < 0) || (booleanOperation > 3))
1286            throw new IllegalArgumentException
1287                (CONTAINS_EX.replace("BOOL", "" + booleanOperation));
1288
1289        int count = (booleanOperation == XOR) ? 0 : cmpStrs.length;
1290
1291        cmpStrs = cmpStrs.clone();
1292
1293        // Iterate each character in the String, and use it as the start to "regionMatches"
1294        for (int i=l.start; i < l.end; i++)
1295
1296            INNER:
1297            // Iterate each of the "compareison strings" - each are passed to "regionMatches"
1298            for (int j=0; j < cmpStrs.length; j++)
1299
1300                // The Strings in Compare-Str are sometimes set to null, if they are skip them
1301                if (cmpStrs[j] != null)
1302
1303                    if (srcStr.regionMatches(ignoresCase, i, cmpStrs[j], 0, cmpStrs[j].length()))
1304
1305                        switch (booleanOperation)
1306                        {
1307                            case AND    :   cmpStrs[j]=null;    // no need to check for it again
1308
1309                                            if (--count == 0)   return true;
1310                                            else                continue INNER; // test-all cmpStrs
1311
1312                            case XOR    :   cmpStrs[j]=null;    // no need to check for it again
1313
1314                                            if (++count > 1)    return false;
1315                                            else                continue INNER; // test-all cmpStrs
1316
1317                            case OR     :   return true;
1318
1319                            case NAND   :   return false;
1320                        }
1321
1322        switch(booleanOperation)
1323        {
1324            case AND    : return false;
1325            case OR     : return false;
1326            case XOR    : return count == 1;
1327            case NAND   : return true;
1328        }
1329
1330        // Already Checked boolean-operation, there is no way to reach this line of code.
1331        throw new UnreachableError();
1332    }
1333
1334    /**
1335     * Implementing a 'contains' is more efficient by performing only one loop iteration.
1336     * Therefore, the methods that implement some variant of the {@code String.contains} method
1337     * will use this general-purpose {@code contains}-testing method.
1338     * @param ignoresCase This is used to signify whether to ignore case when performing the
1339     * {@code String}-comparison operations.
1340     * @param booleanOperation This is designed to take one of four values.  This method will
1341     * throw an exception if it does not.
1342     * @param srcStr Any non-null instance of {@code java.lang.String}
1343     * @param cmpStrs This is one (or many) java {@code String's}, each of which shall be
1344     * compared to the ending of {@code 'srcStr'}.
1345     * @return The appropriate response based on the inputs for boolean logic.
1346     */
1347    protected static boolean CONTAINS(
1348        boolean ignoresCase, byte booleanOperation, String srcStr, String[] cmpStrs
1349    )
1350    {
1351        // Check each of the compare-strs first.  It will provide a better exception message
1352        for (String s : cmpStrs)
1353            if (s == null) throw new NullPointerException
1354                ("One of the elements of 'compareStr' was null");
1355
1356        if ((booleanOperation < 0) || (booleanOperation > 3))
1357            throw new IllegalArgumentException
1358                (CONTAINS_EX.replace("BOOL", "" + booleanOperation));
1359
1360        int count   = (booleanOperation == XOR) ? 0 : cmpStrs.length;
1361        int len     = srcStr.length();
1362
1363        cmpStrs = cmpStrs.clone();
1364
1365        // Iterate each character in the String, and use it as the start to "regionMatches"
1366        for (int i=0; i < len; i++)
1367
1368            INNER:
1369            // Iterate each of the "compareison strings" - each are passed to "regionMatches"
1370            for (int j=0; j < cmpStrs.length; j++)
1371
1372                // The Strings in Compare-Str are sometimes set to null, if they are skip them
1373                if (cmpStrs[j] != null)
1374
1375                    if (srcStr.regionMatches(ignoresCase, i, cmpStrs[j], 0, cmpStrs[j].length()))
1376
1377                        switch (booleanOperation)
1378                        {
1379                            case AND    :   cmpStrs[j]=null;    // no need to check for it again
1380
1381                                            if (--count == 0)   return true;
1382                                            else                continue INNER; // test-all cmpStrs
1383
1384                            case XOR    :   cmpStrs[j]=null;    // no need to check for it again
1385
1386                                            if (++count > 1)    return false;
1387                                            else                continue INNER; // test-all cmpStrs
1388
1389                            case OR     :   return true;
1390
1391                            case NAND   :   return false;
1392                        }
1393
1394        switch(booleanOperation)
1395        {
1396            case AND    : return false;
1397            case OR     : return false;
1398            case XOR    : return count == 1;
1399            case NAND   : return true;
1400        }
1401
1402        // Already Checked boolean-operation, there is no way to reach this line of code.
1403        throw new UnreachableError();
1404    }
1405}