/*
 * Copyright (c) 2002, 2026, Oracle and/or its affiliates.
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by
 * the Free Software Foundation.
 *
 * This program is designed to work with certain software that is licensed under separate terms, as designated in a particular file or component or in
 * included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the
 * separately licensed software that they have either included with the program or referenced in the documentation.
 *
 * Without limiting anything contained in the foregoing, this file, which is part of MySQL Connector/J, is also subject to the Universal FOSS Exception,
 * version 1.0, a copy of which can be found at http://oss.oracle.com/licenses/universal-foss-exception.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

package com.mysql.cj.util;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import org.junit.jupiter.api.Test;

import com.mysql.cj.jdbc.DatabaseMetaData;

import testsuite.BaseTestCase;

public class StringUtilsTest extends BaseTestCase {

    /**
     * Tests StringUtil.indexOfIgnoreCase() methods
     *
     * @throws Exception
     */
    @Test
    public void testIndexOfIgnoreCase() throws Exception {
        final String markerStart = "\"'`(";
        final String markerEnd = "\"'`)";

        String searchIn;
        String[] searchInMulti;
        String searchFor;
        String[] searchForMulti;
        int[] expectedIdx;

        int pos;
        Set<SearchMode> searchMode;

        /*
         * A. test indexOfIgnoreCase(String searchIn, String searchFor)
         */
        // basic test set
        assertEquals(-1, StringUtils.indexOfIgnoreCase(null, null));
        assertEquals(-1, StringUtils.indexOfIgnoreCase(null, "abc"));
        assertEquals(-1, StringUtils.indexOfIgnoreCase("abc", null));
        assertEquals(-1, StringUtils.indexOfIgnoreCase("abc", ""));
        assertEquals(-1, StringUtils.indexOfIgnoreCase("abc", "bcd"));
        assertEquals(0, StringUtils.indexOfIgnoreCase("abc", "abc"));
        assertEquals(3, StringUtils.indexOfIgnoreCase("abc d efg", " d "));

        // exhaustive test set
        searchIn = "A strange STRONG SsStRiNg to be searched in";
        searchForMulti = new String[] { "STR", "sstr", "Z", "a str", " in", "b" };
        expectedIdx = new int[] { 2, 18, -1, 0, 40, 29 };
        for (int i = 0; i < searchForMulti.length; i++) {
            assertEquals(expectedIdx[i], StringUtils.indexOfIgnoreCase(searchIn, searchForMulti[i]), "Test A." + i);
        }

        /*
         * B. test indexOfIgnoreCase(int startingPosition, String searchIn, String searchFor)
         */
        // basic test set
        assertEquals(-1, StringUtils.indexOfIgnoreCase(3, null, null));
        assertEquals(-1, StringUtils.indexOfIgnoreCase(3, null, "abc"));
        assertEquals(-1, StringUtils.indexOfIgnoreCase(3, "abc", null));
        assertEquals(-1, StringUtils.indexOfIgnoreCase(3, "abc", ""));
        assertEquals(-1, StringUtils.indexOfIgnoreCase(3, "abc", "bcd"));
        assertEquals(3, StringUtils.indexOfIgnoreCase(3, "abcabc", "abc"));
        assertEquals(3, StringUtils.indexOfIgnoreCase("abc d efg", " d "));

        // exhaustive test set
        searchIn = "A strange STRONG SsStRiNg to be searched in";
        searchForMulti = new String[] { "STR", "sstr", "Z", "a str", " in", "b" };
        expectedIdx = new int[] { 10, 18, -1, -1, 40, 29 };
        for (int i = 0; i < searchForMulti.length; i++) {
            assertEquals(expectedIdx[i], StringUtils.indexOfIgnoreCase(3, searchIn, searchForMulti[i]), "Test B." + i);
        }

        /*
         * C. test indexOfIgnoreCase(int startingPosition, String searchIn, String searchFor, String openingMarkers, String closingMarkers, Set<SearchMode>
         * searchMode) using search modes SearchMode.__MRK_WS or SearchMode.__BSE_MRK_WS
         */
        // basic test set
        assertThrows(IllegalArgumentException.class, "The source string must not be null.", () -> {
            StringUtils.indexOfIgnoreCase(0, null, (String) null, markerStart, markerEnd, SearchMode.__BSE_MRK_WS);
            return null;
        });
        assertThrows(IllegalArgumentException.class, "The source string must not be null.", () -> {
            StringUtils.indexOfIgnoreCase(0, null, "abc", markerStart, markerEnd, SearchMode.__BSE_MRK_WS);
            return null;
        });
        assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "abc", (String) null, markerStart, markerEnd, SearchMode.__BSE_MRK_WS));
        assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "abc", "", markerStart, markerEnd, SearchMode.__BSE_MRK_WS));
        assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "abc", "bcd", markerStart, markerEnd, SearchMode.__BSE_MRK_WS));
        assertEquals(6, StringUtils.indexOfIgnoreCase(0, "ab -- abc", "abc", markerStart, markerEnd, SearchMode.__BSE_MRK_WS));
        assertEquals(5, StringUtils.indexOfIgnoreCase(0, "ab # abc", "abc", markerStart, markerEnd, SearchMode.__BSE_MRK_WS));
        assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "ab/*/* /c", "abc", markerStart, markerEnd, SearchMode.__BSE_MRK_WS));
        assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "ab/**/c", "abc", markerStart, markerEnd, SearchMode.__BSE_MRK_WS));
        assertEquals(5, StringUtils.indexOfIgnoreCase(0, "ab/* abc */c", "abc", markerStart, markerEnd, SearchMode.__BSE_MRK_WS));
        assertEquals(0, StringUtils.indexOfIgnoreCase(0, "abc", "abc", markerStart, markerEnd, SearchMode.__BSE_MRK_WS));
        assertEquals(3, StringUtils.indexOfIgnoreCase(0, "abc d efg", " d ", markerStart, markerEnd, SearchMode.__BSE_MRK_WS));

        // exhaustive test set
        searchInMulti = new String[] { "A \"strange \"STRONG SsStRiNg to be searched in", "A 'strange 'STRONG SsStRiNg to be searched in",
                "A `strange `STRONG SsStRiNg to be searched in", "A (strange )STRONG SsStRiNg to be searched in" };
        searchForMulti = new String[] { "STR", "sstr", "Z", "a str", " in", "b" };
        expectedIdx = new int[] { 12, 20, -1, -1, 42, 31 };
        for (int i = 0; i < searchForMulti.length; i++) {
            for (int j = 0; j < searchInMulti.length; j++) {
                // multiple markers
                assertEquals(expectedIdx[i], StringUtils.indexOfIgnoreCase(0, searchInMulti[j], searchForMulti[i], markerStart, markerEnd, SearchMode.__MRK_WS),
                        "Test C." + j + "." + i);
                assertEquals(expectedIdx[i],
                        StringUtils.indexOfIgnoreCase(0, searchInMulti[j], searchForMulti[i], markerStart, markerEnd, SearchMode.__BSE_MRK_WS),
                        "Test C." + j + "." + i);

                // single marker
                assertEquals(expectedIdx[i], StringUtils.indexOfIgnoreCase(0, searchInMulti[j], searchForMulti[i], markerStart.substring(j, j + 1),
                        markerEnd.substring(j, j + 1), SearchMode.__MRK_WS), "Test C." + j + "." + i);
                assertEquals(expectedIdx[i], StringUtils.indexOfIgnoreCase(0, searchInMulti[j], searchForMulti[i], markerStart.substring(j, j + 1),
                        markerEnd.substring(j, j + 1), SearchMode.__BSE_MRK_WS), "Test C." + j + "." + i);
            }
        }

        searchIn = "A (`'\"strange \"'`)STRONG SsStRiNg to be searched in";
        searchForMulti = new String[] { "STR", "sstr", "Z", "a str", " in", "b" };
        expectedIdx = new int[] { 18, 26, -1, -1, 48, 37 };
        for (int i = 0; i < searchForMulti.length; i++) {
            // multiple markers
            assertEquals(expectedIdx[i], StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti[i], markerStart, markerEnd, SearchMode.__BSE_MRK_WS),
                    "Test C.4." + i);
            // single marker
            assertEquals(expectedIdx[i], StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti[i], "'", "'", SearchMode.__BSE_MRK_WS), "Test C.5." + i);
        }

        searchIn = "A 'strange \\''STRONG \\`SsSTRING\\\" to be searched in";
        searchForMulti = new String[] { "STR", "sstr", "Z", "a str", " in", "b" };
        expectedIdx = new int[] { 14, 24, -1, -1, 48, 37 };
        for (int i = 0; i < searchForMulti.length; i++) {
            // multiple markers
            assertEquals(expectedIdx[i], StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti[i], markerStart, markerEnd, SearchMode.__BSE_MRK_WS),
                    "Test C.6." + i);
            // single marker
            assertEquals(expectedIdx[i], StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti[i], "'", "'", SearchMode.__BSE_MRK_WS), "Test C.7." + i);
        }

        /*
         * D. test indexOfIgnoreCase(int startingPosition, String searchIn, String searchFor, String openingMarkers, String closingMarkers, Set<SearchMode>
         * searchMode) using combined and single search modes
         */
        // basic test set
        assertThrows(IllegalArgumentException.class, "The source string must not be null.", () -> {
            StringUtils.indexOfIgnoreCase(0, null, (String) null, markerStart, markerEnd, SearchMode.__FULL);
            return null;
        });
        assertThrows(IllegalArgumentException.class, "The source string must not be null.", () -> {
            StringUtils.indexOfIgnoreCase(0, null, "abc", markerStart, markerEnd, SearchMode.__FULL);
            return null;
        });
        assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "abc", (String) null, markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "abc", "", markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "abc", "bcd", markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "ab -- abc", "abc", markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "ab # abc", "abc", markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "ab/*/* /c", "abc", markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "ab/**/c", "abc", markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "ab/* abc */c", "abc", markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(0, StringUtils.indexOfIgnoreCase(0, "abc", "abc", markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(3, StringUtils.indexOfIgnoreCase(0, "abc d efg", " d ", markerStart, markerEnd, SearchMode.__FULL));

        // exhaustive test set
        searchIn = "/* MySQL01 *//* MySQL02 */ \"MySQL03\" /* MySQL04 */-- MySQL05\n/* MySQL06 *//* MySQL07 */ 'MySQL08' /* MySQL09 */-- # MySQL10\r\n"
                + "/* MySQL11 *//* MySQL12 */ `MySQL13` /* MySQL14 */# MySQL15\r\n/* MySQL16 *//* MySQL17 */ (MySQL18) /* MySQL19 */# -- MySQL20 \n"
                + "/* MySQL21 *//* MySQL22 */ \\MySQL23--;/*!12345 MySQL24 */   /*+ MySQL25 */ MySQL26 --";
        searchFor = "mYSql";

        // D.1. different markers in method arguments
        pos = StringUtils.indexOfIgnoreCase(0, searchIn, searchFor, null, null, SearchMode.__BSE_COM_MYM_HNT_WS);
        assertEquals(3, testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos));
        pos = StringUtils.indexOfIgnoreCase(0, searchIn, searchFor, "", "", SearchMode.__FULL);
        assertEquals(3, testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos));
        pos = StringUtils.indexOfIgnoreCase(0, searchIn, searchFor, "'`(", "'`)", SearchMode.__FULL);
        assertEquals(3, testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos));
        pos = StringUtils.indexOfIgnoreCase(0, searchIn, searchFor, "\"`(", "\"`)", SearchMode.__FULL);
        assertEquals(8, testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos));
        pos = StringUtils.indexOfIgnoreCase(0, searchIn, searchFor, "\"'(", "\"')", SearchMode.__FULL);
        assertEquals(13, testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos));
        pos = StringUtils.indexOfIgnoreCase(0, searchIn, searchFor, "`'\"", "`'\"", SearchMode.__FULL);
        assertEquals(18, testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos));

        // D.2a. search mode: all but SearchMode.SKIP_BETWEEN_MARKERS
        searchMode = SearchMode.__BSE_COM_MYM_HNT_WS;
        pos = 0;
        expectedIdx = new int[] { 3, 8, 13, 18, 23, 24, 26, -1 };
        for (int i = 0; i < expectedIdx.length; i++, pos++) {
            pos = StringUtils.indexOfIgnoreCase(pos, searchIn, searchFor, markerStart, markerEnd, searchMode);
            assertEquals(expectedIdx[i], testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos), "Test D.2a." + i);
        }
        // D.2b. search mode: only SearchMode.SKIP_BETWEEN_MARKERS
        searchMode = EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS);
        pos = 0;
        expectedIdx = new int[] { 1, 2, 4, 5, 6, 7, 9, 10, 11, 12, 14, 15, 16, 17, 19, 20, 21, 22, 23, 24, 25, 26, -1 };
        for (int i = 0; i < expectedIdx.length; i++, pos++) {
            pos = StringUtils.indexOfIgnoreCase(pos, searchIn, searchFor, markerStart, markerEnd, searchMode);
            assertEquals(expectedIdx[i], testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos), "Test D.2b." + i);
        }

        // D.3a. search mode: all but SearchMode.SKIP_BLOCK_COMMENTS
        searchMode = EnumSet.of(SearchMode.ALLOW_BACKSLASH_ESCAPE, SearchMode.SKIP_BETWEEN_MARKERS, SearchMode.SKIP_LINE_COMMENTS,
                SearchMode.SKIP_MYSQL_MARKERS, SearchMode.SKIP_HINT_BLOCKS, SearchMode.SKIP_WHITE_SPACE);
        pos = 0;
        expectedIdx = new int[] { 1, 2, 4, 6, 7, 9, 11, 12, 14, 16, 17, 19, 21, 22, 23, 24, 26, -1 };
        for (int i = 0; i < expectedIdx.length; i++, pos++) {
            pos = StringUtils.indexOfIgnoreCase(pos, searchIn, searchFor, markerStart, markerEnd, searchMode);
            assertEquals(expectedIdx[i], testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos), "Test D.3a." + i);
        }
        // D.3b. search mode: only SearchMode.SKIP_BLOCK_COMMENTS
        searchMode = EnumSet.of(SearchMode.SKIP_BLOCK_COMMENTS);
        pos = 0;
        expectedIdx = new int[] { 3, 5, 8, 10, 13, 15, 18, 20, 23, 24, 25, 26, -1 };
        for (int i = 0; i < expectedIdx.length; i++, pos++) {
            pos = StringUtils.indexOfIgnoreCase(pos, searchIn, searchFor, markerStart, markerEnd, searchMode);
            assertEquals(expectedIdx[i], testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos), "Test D.3b." + i);
        }

        // D.4a. search mode: all but SearchMode.SKIP_LINE_COMMENTS
        searchMode = EnumSet.of(SearchMode.ALLOW_BACKSLASH_ESCAPE, SearchMode.SKIP_BETWEEN_MARKERS, SearchMode.SKIP_BLOCK_COMMENTS,
                SearchMode.SKIP_MYSQL_MARKERS, SearchMode.SKIP_HINT_BLOCKS, SearchMode.SKIP_WHITE_SPACE);
        pos = 0;
        expectedIdx = new int[] { 5, 10, 15, 20, 23, 24, 26, -1 };
        for (int i = 0; i < expectedIdx.length; i++, pos++) {
            pos = StringUtils.indexOfIgnoreCase(pos, searchIn, searchFor, markerStart, markerEnd, searchMode);
            assertEquals(expectedIdx[i], testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos), "Test D.4a." + i);
        }
        // D.4b. search mode: only SearchMode.SKIP_LINE_COMMENTS
        searchMode = EnumSet.of(SearchMode.SKIP_LINE_COMMENTS);
        pos = 0;
        expectedIdx = new int[] { 1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14, 16, 17, 18, 19, 21, 22, 23, 24, 25, 26, -1 };
        for (int i = 0; i < expectedIdx.length; i++, pos++) {
            pos = StringUtils.indexOfIgnoreCase(pos, searchIn, searchFor, markerStart, markerEnd, searchMode);
            assertEquals(expectedIdx[i], testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos), "Test D.4b." + i);
        }

        // D.5a. search mode: all but SearchMode.SKIP_MYSQL_MARKERS
        searchMode = EnumSet.of(SearchMode.ALLOW_BACKSLASH_ESCAPE, SearchMode.SKIP_BETWEEN_MARKERS, SearchMode.SKIP_BLOCK_COMMENTS,
                SearchMode.SKIP_LINE_COMMENTS, SearchMode.SKIP_HINT_BLOCKS, SearchMode.SKIP_WHITE_SPACE);
        pos = 0;
        expectedIdx = new int[] { 23, 24, 26, -1 };
        for (int i = 0; i < expectedIdx.length; i++, pos++) {
            pos = StringUtils.indexOfIgnoreCase(pos, searchIn, searchFor, markerStart, markerEnd, searchMode);
            assertEquals(expectedIdx[i], testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos), "Test D.5a." + i);
        }
        // D.5b. search mode: only SearchMode.SKIP_MYSQL_MARKERS
        searchMode = EnumSet.of(SearchMode.SKIP_MYSQL_MARKERS);
        pos = 0;
        expectedIdx = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, -1 };
        for (int i = 0; i < expectedIdx.length; i++, pos++) {
            pos = StringUtils.indexOfIgnoreCase(pos, searchIn, searchFor, markerStart, markerEnd, searchMode);
            assertEquals(expectedIdx[i], testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos), "Test D.5b." + i);
        }

        // D.6a. search mode: all but SearchMode.SKIP_HINT_BLOCKS
        searchMode = EnumSet.of(SearchMode.ALLOW_BACKSLASH_ESCAPE, SearchMode.SKIP_BETWEEN_MARKERS, SearchMode.SKIP_BLOCK_COMMENTS,
                SearchMode.SKIP_LINE_COMMENTS, SearchMode.SKIP_MYSQL_MARKERS, SearchMode.SKIP_WHITE_SPACE);
        pos = 0;
        expectedIdx = new int[] { 23, 24, 25, 26, -1 };
        for (int i = 0; i < expectedIdx.length; i++, pos++) {
            pos = StringUtils.indexOfIgnoreCase(pos, searchIn, searchFor, markerStart, markerEnd, searchMode);
            assertEquals(expectedIdx[i], testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos), "Test D.6a." + i);
        }
        // D.6b. search mode: only SearchMode.SKIP_HINT_BLOCKS
        searchMode = EnumSet.of(SearchMode.SKIP_HINT_BLOCKS);
        pos = 0;
        expectedIdx = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 26, -1 };
        for (int i = 0; i < expectedIdx.length; i++, pos++) {
            pos = StringUtils.indexOfIgnoreCase(pos, searchIn, searchFor, markerStart, markerEnd, searchMode);
            assertEquals(expectedIdx[i], testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos), "Test D.6b." + i);
        }

        // D.7a. search mode: all but SearchMode.ALLOW_BACKSLASH_ESCAPE
        pos = StringUtils.indexOfIgnoreCase(0, searchIn, searchFor, markerStart, markerEnd, SearchMode.__MRK_COM_MYM_HNT_WS);
        assertEquals(23, testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos));
        // D.7b. search mode: only allow backslash escape
        searchMode = EnumSet.of(SearchMode.ALLOW_BACKSLASH_ESCAPE);
        pos = 0;
        expectedIdx = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, -1 };
        for (int i = 0; i < expectedIdx.length; i++, pos++) {
            pos = StringUtils.indexOfIgnoreCase(pos, searchIn, searchFor, markerStart, markerEnd, searchMode);
            assertEquals(expectedIdx[i], testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos), "Test D.7b." + i);
        }

        // D.8. all together
        pos = 0;
        expectedIdx = new int[] { 23, 24, 26, -1 };
        for (int i = 0; i < expectedIdx.length; i++, pos++) {
            pos = StringUtils.indexOfIgnoreCase(pos, searchIn, searchFor, markerStart, markerEnd, SearchMode.__FULL);
            assertEquals(expectedIdx[i], testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos), "Test D.8." + i);
        }
        pos = StringUtils.indexOfIgnoreCase(0, searchIn, "YourSQL", markerStart, markerEnd, SearchMode.__FULL);
        assertEquals(-1, testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos));

        // D.9. none
        pos = 0;
        for (int i = 1; i <= 26; i++, pos++) {
            pos = StringUtils.indexOfIgnoreCase(pos, searchIn, searchFor, markerStart, markerEnd, SearchMode.__NONE);
            assertEquals(i, testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos), "Test D.9." + i);
        }
        pos = StringUtils.indexOfIgnoreCase(pos + 1, searchIn, searchFor, markerStart, markerEnd, SearchMode.__NONE);
        assertEquals(-1, testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos));
        pos = StringUtils.indexOfIgnoreCase(0, searchIn, "YourSQL", markerStart, markerEnd, SearchMode.__NONE);
        assertEquals(-1, testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos));

        /*
         * E. test indexOfIgnoreCase(int startingPosition, String searchIn, String searchFor, String openingMarkers, String closingMarkers[, String
         * overridingMarkers], Set<SearchMode> searchMode) illegal markers arguments
         */
        assertThrows(IllegalArgumentException.class,
                "Illegal argument value null for openingMarkers and/or - for closingMarkers. These cannot be null and must have the same length.", () -> {
                    StringUtils.indexOfIgnoreCase(0, "abc", "abc", null, "-", EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS));
                    return null;
                });
        assertThrows(IllegalArgumentException.class,
                "Illegal argument value - for openingMarkers and/or null for closingMarkers. These cannot be null and must have the same length.", () -> {
                    StringUtils.indexOfIgnoreCase(0, "abc", "abc", "-", null, EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS));
                    return null;
                });
        assertThrows(IllegalArgumentException.class,
                "Illegal argument value null for openingMarkers and/or null for closingMarkers. These cannot be null and must have the same length.", () -> {
                    StringUtils.indexOfIgnoreCase(0, "abc", "abc", null, null, EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS));
                    return null;
                });
        assertThrows(IllegalArgumentException.class,
                "Illegal argument value - for openingMarkers and/or -! for closingMarkers. These cannot be null and must have the same length.", () -> {
                    StringUtils.indexOfIgnoreCase(0, "abc", "abc", "-", "-!", EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS));
                    return null;
                });
        assertThrows(IllegalArgumentException.class,
                "Illegal argument value null for overridingMarkers. These cannot be null and must be a sub-set of openingMarkers -!.", () -> {
                    StringUtils.indexOfIgnoreCase(0, "abc", "abc", "-!", "-!", null, EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS));
                    return null;
                });
        assertThrows(IllegalArgumentException.class,
                "Illegal argument value ' for overridingMarkers. These cannot be null and must be a sub-set of openingMarkers -!.", () -> {
                    StringUtils.indexOfIgnoreCase(0, "abc", "abc", "-!", "-!", "'", EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS));
                    return null;
                });

        /*
         * F. test indexOfIgnoreCase(int startingPosition, String searchIn, String searchFor, String openingMarkers, String closingMarkers, Set<SearchMode>
         * searchMode) special cases
         */
        // unclosed, unopened or nested block comments
        searchMode = EnumSet.of(SearchMode.SKIP_BLOCK_COMMENTS);
        searchIn = "one * /* two /* * / three /*/*/ four * /";
        searchForMulti = new String[] { "one", "two", "three", "four" };
        expectedIdx = new int[] { 0, -1, -1, 32 };
        for (int i = 0; i < searchForMulti.length; i++) {
            assertEquals(expectedIdx[i], StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti[i], markerStart, markerEnd, searchMode), "Test F.1." + i);
        }
        searchMode = EnumSet.of(SearchMode.ALLOW_BACKSLASH_ESCAPE, SearchMode.SKIP_BETWEEN_MARKERS, SearchMode.SKIP_LINE_COMMENTS, SearchMode.SKIP_WHITE_SPACE);
        expectedIdx = new int[] { 0, 9, 20, 32 };
        for (int i = 0; i < searchForMulti.length; i++) {
            assertEquals(expectedIdx[i], StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti[i], markerStart, markerEnd, searchMode), "Test F.2." + i);
        }

        // double quoted escapes, including some "noise" chars
        searchMode = EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS);
        searchIn = "one 'two\" ''three''' four\"";
        searchForMulti = new String[] { "one", "two", "three", "four" };
        expectedIdx = new int[] { 0, -1, -1, 21 };
        for (int i = 0; i < searchForMulti.length; i++) {
            assertEquals(expectedIdx[i], StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti[i], markerStart, markerEnd, searchMode), "Test F.3." + i);
        }
        searchMode = SearchMode.__BSE_COM_MYM_HNT_WS;
        expectedIdx = new int[] { 0, 5, 12, 21 };
        for (int i = 0; i < searchForMulti.length; i++) {
            assertEquals(expectedIdx[i], StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti[i], markerStart, markerEnd, searchMode), "Test F.4." + i);
        }

        // nested different opening/closing marker, including some "noise" chars
        searchMode = EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS);
        searchIn = "one (two\"( (three''')) )four)";
        searchForMulti = new String[] { "one", "two", "three", "four" };
        expectedIdx = new int[] { 0, -1, -1, 24 };
        for (int i = 0; i < searchForMulti.length; i++) {
            assertEquals(expectedIdx[i], StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti[i], markerStart, markerEnd, searchMode), "Test F.5." + i);
        }
        searchMode = SearchMode.__BSE_COM_MYM_HNT_WS;
        expectedIdx = new int[] { 0, 5, 12, 24 };
        for (int i = 0; i < searchForMulti.length; i++) {
            assertEquals(expectedIdx[i], StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti[i], markerStart, markerEnd, searchMode), "Test F.6." + i);
        }

        /*
         * G. test indexOfIgnoreCase(int startingPosition, String searchIn, String searchFor[], String openingMarkers, String closingMarkers, Set<SearchMode>
         * searchMode) using all combined search modes
         */
        // basic test set
        assertThrows(IllegalArgumentException.class, "The source string must not be null.", () -> {
            StringUtils.indexOfIgnoreCase(0, null, (String[]) null, markerStart, markerEnd, SearchMode.__FULL);
            return null;
        });
        assertThrows(IllegalArgumentException.class, "The source string must not be null.", () -> {
            StringUtils.indexOfIgnoreCase(0, null, new String[] { "abc" }, markerStart, markerEnd, SearchMode.__FULL);
            return null;
        });
        assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "abc", (String[]) null, markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "abc", new String[] {}, markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "abc", new String[] { "", "" }, markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "abc -- d", new String[] { "c", "d" }, markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(0, StringUtils.indexOfIgnoreCase(0, "abc", new String[] { "abc" }, markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "abc d   efg h", new String[] { " d ", " efg" }, markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(3, StringUtils.indexOfIgnoreCase(0, "abc d   efg h", new String[] { " d ", "efg" }, markerStart, markerEnd, SearchMode.__FULL));

        // exhaustive test set
        searchForMulti = new String[] { "ONE", "two", "ThrEE" };

        // 1. simple strings
        assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "onetwothee", searchForMulti, markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "one one one one         two", searchForMulti, markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(11, StringUtils.indexOfIgnoreCase(0, "onetwothee one  two  three", searchForMulti, markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(20, StringUtils.indexOfIgnoreCase(0, "/* one two three */ one two three", searchForMulti, markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(38, StringUtils.indexOfIgnoreCase(0, "/* one two three *//* one two three */one two three", searchForMulti, markerStart, markerEnd,
                SearchMode.__FULL));
        assertEquals(7, StringUtils.indexOfIgnoreCase(0, "/*one*/one/*two*/two/*three*/three", searchForMulti, markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(0, StringUtils.indexOfIgnoreCase(0, "one/*one*/two/*two*/three/*three*/", searchForMulti, markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(16, StringUtils.indexOfIgnoreCase(0, "# one two three\none two three", searchForMulti, markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(17, StringUtils.indexOfIgnoreCase(0, "-- one two three\none two three", searchForMulti, markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(22, StringUtils.indexOfIgnoreCase(0, "/* one two three */--;one two three", searchForMulti, markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(4, StringUtils.indexOfIgnoreCase(0, "/*! one two three */--;one two three", searchForMulti, markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(9,
                StringUtils.indexOfIgnoreCase(0, "/*!50616 one two three */--;one two three", searchForMulti, markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(16, StringUtils.indexOfIgnoreCase(0, "\"one two three\" one two three", searchForMulti, markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(16, StringUtils.indexOfIgnoreCase(0, "'one two three' one two three", searchForMulti, markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(16, StringUtils.indexOfIgnoreCase(0, "`one two three` one two three", searchForMulti, markerStart, markerEnd, SearchMode.__FULL));
        assertEquals(16, StringUtils.indexOfIgnoreCase(0, "(one two three) one two three", searchForMulti, markerStart, markerEnd, SearchMode.__FULL));

        assertEquals(3, StringUtils.indexOfIgnoreCase(0, "/* one   two   three */ one two three", searchForMulti, markerStart, markerEnd, SearchMode.__NONE));
        assertEquals(2, StringUtils.indexOfIgnoreCase(0, "# one   two   three\none two three", searchForMulti, markerStart, markerEnd, SearchMode.__NONE));
        assertEquals(3, StringUtils.indexOfIgnoreCase(0, "-- one   two   three\none two three", searchForMulti, markerStart, markerEnd, SearchMode.__NONE));

        // 2. complex string
        searchIn = "/* one  two  three *//* one two three */ one 'two' three -- \"one/* one */two  three\" one owt   three\n"
                + "onetwothree # 'one/* one */two  three' one owt   three\noneone /* two *//* three */ -- `one/* one */two  three` one two   three\n"
                + "two -- three\n\n\n/* three */threethree";

        printRuler(searchIn);
        // 2.1. skip all
        assertEquals(159, StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti, markerStart, markerEnd, SearchMode.__FULL));
        // 2.2. search within block comments
        searchMode = EnumSet.of(SearchMode.ALLOW_BACKSLASH_ESCAPE, SearchMode.SKIP_BETWEEN_MARKERS, SearchMode.SKIP_LINE_COMMENTS, SearchMode.SKIP_WHITE_SPACE);
        assertEquals(3, StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti, markerStart, markerEnd, searchMode));
        assertEquals(3, StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti, markerStart, markerEnd, SearchMode.__NONE));
        // 2.3. search within line comments and unidentified markers
        searchMode = EnumSet.of(SearchMode.ALLOW_BACKSLASH_ESCAPE, SearchMode.SKIP_BETWEEN_MARKERS, SearchMode.SKIP_BLOCK_COMMENTS,
                SearchMode.SKIP_WHITE_SPACE);
        assertEquals(61, StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti, "'`(", "'`)", searchMode));
        assertEquals(116, StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti, "\"`(", "\"`)", searchMode));
        assertEquals(188, StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti, "\"'(", "\"')", searchMode));
        assertEquals(212, StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti, markerStart, markerEnd, searchMode));

        /*
         * H. test indexOfIgnoreCase(int startingPosition, String searchIn, String searchFor[], String openingMarkers, String closingMarkers, Set<SearchMode>
         * searchMode) illegal markers arguments
         */
        assertThrows(IllegalArgumentException.class,
                "Illegal argument value null for openingMarkers and/or - for closingMarkers. These cannot be null and must have the same length.", () -> {
                    StringUtils.indexOfIgnoreCase(0, "abc", new String[] { "abc" }, null, "-", EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS));
                    return null;
                });
        assertThrows(IllegalArgumentException.class,
                "Illegal argument value - for openingMarkers and/or null for closingMarkers. These cannot be null and must have the same length.", () -> {
                    StringUtils.indexOfIgnoreCase(0, "abc", new String[] { "abc" }, "-", null, EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS));
                    return null;
                });
        assertThrows(IllegalArgumentException.class,
                "Illegal argument value null for openingMarkers and/or null for closingMarkers. These cannot be null and must have the same length.", () -> {
                    StringUtils.indexOfIgnoreCase(0, "abc", new String[] { "abc" }, null, null, EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS));
                    return null;
                });
        assertThrows(IllegalArgumentException.class,
                "Illegal argument value - for openingMarkers and/or -! for closingMarkers. These cannot be null and must have the same length.", () -> {
                    StringUtils.indexOfIgnoreCase(0, "abc", new String[] { "abc" }, "-", "-!", EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS));
                    return null;
                });
    }

    private int testIndexOfIgnoreCaseMySQLIndexMarker(String source, int pos) {
        return pos == -1 ? -1 : Integer.parseInt(source.substring(pos + 5, pos + 7));
    }

    private static void printRuler(String txt) {
        System.out.printf("      0    5   10   15   20   25   30   35   40   45   50   55   60   65   70   75   80   85   90   95  100%n");
        System.out.printf("      |----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|%n");

        int count = 0;
        for (String line : txt.split("\n")) {
            System.out.printf("%4d+ %s%n", count, line);
            count += line.length() + 1;
        }
    }

    /**
     * Tests StringUtil.indexOfQuoteDoubleAware() method
     *
     * @throws Exception
     */
    @Test
    public void testIndexOfQuoteDoubleAware() throws Exception {
        final String[] searchInDoubledQt = new String[] { "A 'strange' \"STRONG\" `SsStRiNg` to be searched in",
                "A ''strange'' \"\"STRONG\"\" ``SsStRiNg`` to be searched in" };

        assertEquals(-1, StringUtils.indexOfQuoteDoubleAware(null, '\'', 0));
        assertEquals(-1, StringUtils.indexOfQuoteDoubleAware("abc", '\"', 0));
        assertEquals(-1, StringUtils.indexOfQuoteDoubleAware("abc", 'e', 0));
        assertEquals(0, StringUtils.indexOfQuoteDoubleAware("abc", 'a', 0));

        int qtPos = 0;
        assertEquals(2, qtPos = StringUtils.indexOfQuoteDoubleAware(searchInDoubledQt[0], '\'', 0));
        assertEquals(10, qtPos = StringUtils.indexOfQuoteDoubleAware(searchInDoubledQt[0], '\'', qtPos + 1));
        assertEquals(-1, StringUtils.indexOfQuoteDoubleAware(searchInDoubledQt[0], '\'', qtPos + 1));
        assertEquals(12, qtPos = StringUtils.indexOfQuoteDoubleAware(searchInDoubledQt[0], '"', 0));
        assertEquals(19, qtPos = StringUtils.indexOfQuoteDoubleAware(searchInDoubledQt[0], '"', qtPos + 1));
        assertEquals(-1, StringUtils.indexOfQuoteDoubleAware(searchInDoubledQt[0], '"', qtPos + 1));
        assertEquals(21, qtPos = StringUtils.indexOfQuoteDoubleAware(searchInDoubledQt[0], '`', 0));
        assertEquals(30, qtPos = StringUtils.indexOfQuoteDoubleAware(searchInDoubledQt[0], '`', qtPos + 1));
        assertEquals(-1, StringUtils.indexOfQuoteDoubleAware(searchInDoubledQt[0], '`', qtPos + 1));

        assertEquals(-1, StringUtils.indexOfQuoteDoubleAware(searchInDoubledQt[1], '\'', 0));
        assertEquals(-1, StringUtils.indexOfQuoteDoubleAware(searchInDoubledQt[1], '"', 0));
        assertEquals(-1, StringUtils.indexOfQuoteDoubleAware(searchInDoubledQt[1], '`', 0));
    }

    /**
     * Tests StringUtil.appendAsHex() methods.
     *
     * @throws Exception
     */
    @Test
    public void testAppendAsHex() throws Exception {
        final byte[] testBytes = new byte[256];
        final int[] testInts = new int[] { Integer.MIN_VALUE, -1023, 0, 511, 512, 0x100FF, 0x10000FF, Integer.MAX_VALUE };
        StringBuilder builder;

        for (int i = 0; i < 256; i++) {
            testBytes[i] = (byte) i;
        }

        // test StringUtils.appendAsHex(StringBuilder, byte[])
        builder = new StringBuilder(1024);
        builder.append("0x");
        for (byte b : testBytes) {
            builder.append(String.format("%02x", b));
        }
        String expected = builder.toString();

        builder = new StringBuilder(1024);
        StringUtils.appendAsHex(builder, testBytes);

        assertEquals(expected, builder.toString(), "Wrong byte[] to HEX convertion");

        // test StringUtils.appendAsHex(StringBuilder, int)
        for (int i : testInts) {
            builder = new StringBuilder(1024);
            StringUtils.appendAsHex(builder, i);
            assertEquals("0x" + Integer.toHexString(i), builder.toString(), "Wrong int to HEX convertion");
        }
    }

    /**
     * Tests StringUtil.getBytes() methods.
     *
     * @throws Exception
     */
    @Test
    public void testGetBytes() throws Exception {
        final int offset = 8;
        final int length = 13;
        final String text = "MySQL ≈ 𝄞 for my ears";
        final String textPart = text.substring(offset, offset + length);
        final String textWrapped = "`MySQL ≈ 𝄞 for my ears`";
        final char[] textAsCharArray = text.toCharArray();

        byte[] asBytesFromString;
        byte[] asBytesFromStringUtils;

        asBytesFromString = text.getBytes();
        asBytesFromStringUtils = StringUtils.getBytes(text);
        assertByteArrayEquals("Default Charset: " + Charset.defaultCharset().name(), asBytesFromString, asBytesFromStringUtils);

        asBytesFromString = textPart.getBytes();
        asBytesFromStringUtils = StringUtils.getBytes(text, offset, length);
        assertByteArrayEquals("Default Charset: " + Charset.defaultCharset().name(), asBytesFromString, asBytesFromStringUtils);

        Map<String, Charset> charsetMap = Charset.availableCharsets();
        for (Charset cs : charsetMap.values()) {
            if (cs.canEncode()) {
                asBytesFromString = text.getBytes(cs.name());

                asBytesFromStringUtils = StringUtils.getBytes(text, cs.name());
                assertByteArrayEquals("Custom Charset: " + cs.name(), asBytesFromString, asBytesFromStringUtils);
                asBytesFromStringUtils = StringUtils.getBytes(textAsCharArray, cs.name());
                assertByteArrayEquals("Custom Charset: " + cs.name(), asBytesFromString, asBytesFromStringUtils);

                asBytesFromStringUtils = StringUtils.getBytes(text, cs.name());
                assertByteArrayEquals("Custom Charset: " + cs.name(), asBytesFromString, asBytesFromStringUtils);
                asBytesFromStringUtils = StringUtils.getBytes(textAsCharArray, cs.name());
                assertByteArrayEquals("Custom Charset: " + cs.name(), asBytesFromString, asBytesFromStringUtils);

                asBytesFromString = textPart.getBytes(cs.name());

                asBytesFromStringUtils = StringUtils.getBytes(text, offset, length, cs.name());
                assertByteArrayEquals("Custom Charset: " + cs.name(), asBytesFromString, asBytesFromStringUtils);
                asBytesFromStringUtils = StringUtils.getBytes(textAsCharArray, offset, length, cs.name());
                assertByteArrayEquals("Custom Charset: " + cs.name(), asBytesFromString, asBytesFromStringUtils);

                asBytesFromStringUtils = StringUtils.getBytes(text, offset, length, cs.name());
                assertByteArrayEquals("Custom Charset: " + cs.name(), asBytesFromString, asBytesFromStringUtils);
                asBytesFromStringUtils = StringUtils.getBytes(textAsCharArray, offset, length, cs.name());
                assertByteArrayEquals("Custom Charset: " + cs.name(), asBytesFromString, asBytesFromStringUtils);

                asBytesFromString = textWrapped.getBytes(cs.name());

                asBytesFromStringUtils = StringUtils.getBytesWrapped(text, '`', '`', cs.name());
                assertByteArrayEquals("Custom Charset: " + cs.name(), asBytesFromString, asBytesFromStringUtils);
            }
        }
    }

    /**
     * Tests StringUtil.quoteIdentifier() and StringUtil.unQuoteIdentifier() methods using backquote marks.
     *
     * @throws Exception
     */
    @Test
    public void testQuoteUnQuoteIdentifierWithBackQuote() throws Exception {
        // Base set of identifiers
        String[] identifiers = new String[] { "abcxyz", "abc`xyz", "abc``xyz", "abc```xyz", // 1..4
                "`abcxyz`", "`abc`xyz`", "`abc``xyz`", "`abc```xyz`",                       // 5..8
                "``abcxyz``", "``abc`xyz``", "``abc``xyz``", "``abc```xyz``",               // 9..12
                "```abcxyz```", "```abc`xyz```", "```abc``xyz```", "```abc```xyz```",       // 13..16
                "`abcxyz", "``abcxyz", "```abcxyz", "abcxyz`", "abcxyz``", "abcxyz```",     // 17..22
                "``abcxyz`", "``abc`xyz`", "``abc``xyz`", "``abc```xyz`",                   // 23..26
                "```abcxyz`", "```abc`xyz`", "```abc``xyz`", "```abc```xyz`",               // 27..30
                "`abcxyz``", "`abc`xyz``", "`abc``xyz``", "`abc```xyz``",                   // 31..34
                "`abcxyz```", "`abc`xyz```", "`abc``xyz```", "`abc```xyz```"                // 35..38
        };

        // Identifiers unquoted
        String[] identifiersUnQuoted = new String[] { "abcxyz", "abc`xyz", "abc``xyz", "abc```xyz", // 1..4
                "abcxyz", "`abc`xyz`", "abc`xyz", "`abc```xyz`",                                    // 5..8
                "``abcxyz``", "``abc`xyz``", "``abc``xyz``", "``abc```xyz``",                       // 9..12
                "`abcxyz`", "```abc`xyz```", "`abc`xyz`", "```abc```xyz```",                        // 13..16
                "`abcxyz", "``abcxyz", "```abcxyz", "abcxyz`", "abcxyz``", "abcxyz```",             // 17..22
                "``abcxyz`", "``abc`xyz`", "``abc``xyz`", "``abc```xyz`",                           // 23..26
                "`abcxyz", "```abc`xyz`", "`abc`xyz", "```abc```xyz`",                              // 27..30
                "`abcxyz``", "`abc`xyz``", "`abc``xyz``", "`abc```xyz``",                           // 31..34
                "abcxyz`", "`abc`xyz```", "abc`xyz`", "`abc```xyz```"                               // 35..38
        };

        // Identifiers quoted in non-pedantic mode
        String[] identifiersQuotedNonPedantic = new String[] { "`abcxyz`", "`abc``xyz`", "`abc````xyz`", "`abc``````xyz`", // 1..4
                "`abcxyz`", "```abc``xyz```", "`abc``xyz`", "```abc``````xyz```",                                          // 5..8
                "`````abcxyz`````", "`````abc``xyz`````", "`````abc````xyz`````", "`````abc``````xyz`````",                // 9..12
                "```abcxyz```", "```````abc``xyz```````", "```abc``xyz```", "```````abc``````xyz```````",                  // 13..16
                "```abcxyz`", "`````abcxyz`", "```````abcxyz`", "`abcxyz```", "`abcxyz`````", "`abcxyz```````",            // 17..22
                "`````abcxyz```", "`````abc``xyz```", "`````abc````xyz```", "`````abc``````xyz```",                        // 23..26
                "```abcxyz`", "```````abc``xyz```", "```abc``xyz`", "```````abc``````xyz```",                              // 27..30
                "```abcxyz`````", "```abc``xyz`````", "```abc````xyz`````", "```abc``````xyz`````",                        // 31..34
                "`abcxyz```", "```abc``xyz```````", "`abc``xyz```", "```abc``````xyz```````"                               // 35..38
        };

        // Identifiers quoted in pedantic mode
        String[] identifiersQuotedPedantic = new String[] { "`abcxyz`", "`abc``xyz`", "`abc````xyz`", "`abc``````xyz`",     // 1..4
                "```abcxyz```", "```abc``xyz```", "```abc````xyz```", "```abc``````xyz```",                                 // 5..8
                "`````abcxyz`````", "`````abc``xyz`````", "`````abc````xyz`````", "`````abc``````xyz`````",                 // 9..12
                "```````abcxyz```````", "```````abc``xyz```````", "```````abc````xyz```````", "```````abc``````xyz```````", // 13..16
                "```abcxyz`", "`````abcxyz`", "```````abcxyz`", "`abcxyz```", "`abcxyz`````", "`abcxyz```````",             // 17..22
                "`````abcxyz```", "`````abc``xyz```", "`````abc````xyz```", "`````abc``````xyz```",                         // 23..26
                "```````abcxyz```", "```````abc``xyz```", "```````abc````xyz```", "```````abc``````xyz```",                 // 27..30
                "```abcxyz`````", "```abc``xyz`````", "```abc````xyz`````", "```abc``````xyz`````",                         // 31..34
                "```abcxyz```````", "```abc``xyz```````", "```abc````xyz```````", "```abc``````xyz```````"                  // 35..38
        };

        // Quoting rules (non-pedantic mode):
        // * identifiers[n] --> identifiersQuotedNonPedantic[n]
        for (int i = 0; i < identifiers.length; i++) {
            assertEquals(identifiersQuotedNonPedantic[i], StringUtils.quoteIdentifier(identifiers[i], '`', false),
                    i + 1 + ". " + identifiers[i] + ". non-pedantic quoting");
            assertEquals(identifiersQuotedNonPedantic[i], StringUtils.quoteIdentifier(identifiers[i], false),
                    i + 1 + ". " + identifiers[i] + ". non-pedantic quoting");
        }

        // Quoting rules (pedantic mode):
        // * identifiers[n] --> identifiersQuotedPedantic[n]
        // * identifiersUnQuoted[n] --> identifiersQuotedNonPedantic[n]
        for (int i = 0; i < identifiers.length; i++) {
            assertEquals(identifiersQuotedPedantic[i], StringUtils.quoteIdentifier(identifiers[i], '`', true),
                    i + 1 + ". " + identifiers[i] + ". pedantic quoting");
            assertEquals(identifiersQuotedPedantic[i], StringUtils.quoteIdentifier(identifiers[i], true), i + 1 + ". " + identifiers[i] + ". pedantic quoting");

            assertEquals(identifiersQuotedNonPedantic[i], StringUtils.quoteIdentifier(identifiersUnQuoted[i], '`', true),
                    i + 1 + ". " + identifiersUnQuoted[i] + ". pedantic quoting");
            assertEquals(identifiersQuotedNonPedantic[i], StringUtils.quoteIdentifier(identifiersUnQuoted[i], true),
                    i + 1 + ". " + identifiersUnQuoted[i] + ". pedantic quoting");
        }

        // Unquoting rules:
        // * identifiers[n] --> identifiersUnQuoted[n]
        // * identifiersQuotedNonPedantic[n] --> identifiersUnQuoted[n]
        // * identifiersQuotedPedantic[n] --> identifiers[n]
        for (int i = 0; i < identifiers.length; i++) {
            assertEquals(identifiersUnQuoted[i], StringUtils.unquoteIdentifier(identifiers[i], '`'), i + 1 + ". " + identifiers[i] + ". unquoting");
            assertEquals(identifiersUnQuoted[i], StringUtils.unquoteIdentifier(identifiersQuotedNonPedantic[i], '`'),
                    i + 1 + ". " + identifiersQuotedNonPedantic[i] + ". non-pedantic unquoting");
            assertEquals(identifiers[i], StringUtils.unquoteIdentifier(identifiersQuotedPedantic[i], '`'),
                    i + 1 + ". " + identifiersQuotedPedantic[i] + ". pedantic unquoting");
        }
    }

    /**
     * Tests StringUtil.quoteIdentifier() and StringUtil.unQuoteIdentifier() methods using double quote marks.
     *
     * @throws Exception
     */
    @Test
    public void testQuoteUnQuoteIdentifierWithDoubleQuote() throws Exception {
        // Base set of identifiers
        String[] identifiers = new String[] { "abcxyz", "abc\"xyz", "abc\"\"xyz", "abc\"\"\"xyz",                   // 1..4
                "\"abcxyz\"", "\"abc\"xyz\"", "\"abc\"\"xyz\"", "\"abc\"\"\"xyz\"",                                 // 5..8
                "\"\"abcxyz\"\"", "\"\"abc\"xyz\"\"", "\"\"abc\"\"xyz\"\"", "\"\"abc\"\"\"xyz\"\"",                 // 9..12
                "\"\"\"abcxyz\"\"\"", "\"\"\"abc\"xyz\"\"\"", "\"\"\"abc\"\"xyz\"\"\"", "\"\"\"abc\"\"\"xyz\"\"\"", // 13..16
                "\"abcxyz", "\"\"abcxyz", "\"\"\"abcxyz", "abcxyz\"", "abcxyz\"\"", "abcxyz\"\"\"",                 // 17..22
                "\"\"abcxyz\"", "\"\"abc\"xyz\"", "\"\"abc\"\"xyz\"", "\"\"abc\"\"\"xyz\"",                         // 23..26
                "\"\"\"abcxyz\"", "\"\"\"abc\"xyz\"", "\"\"\"abc\"\"xyz\"", "\"\"\"abc\"\"\"xyz\"",                 // 27..30
                "\"abcxyz\"\"", "\"abc\"xyz\"\"", "\"abc\"\"xyz\"\"", "\"abc\"\"\"xyz\"\"",                         // 31..34
                "\"abcxyz\"\"\"", "\"abc\"xyz\"\"\"", "\"abc\"\"xyz\"\"\"", "\"abc\"\"\"xyz\"\"\""                  // 35..38
        };

        // Identifiers unquoted
        String[] identifiersUnQuoted = new String[] { "abcxyz", "abc\"xyz", "abc\"\"xyz", "abc\"\"\"xyz", // 1..4
                "abcxyz", "\"abc\"xyz\"", "abc\"xyz", "\"abc\"\"\"xyz\"",                                 // 5..8
                "\"\"abcxyz\"\"", "\"\"abc\"xyz\"\"", "\"\"abc\"\"xyz\"\"", "\"\"abc\"\"\"xyz\"\"",       // 9..12
                "\"abcxyz\"", "\"\"\"abc\"xyz\"\"\"", "\"abc\"xyz\"", "\"\"\"abc\"\"\"xyz\"\"\"",         // 13..16
                "\"abcxyz", "\"\"abcxyz", "\"\"\"abcxyz", "abcxyz\"", "abcxyz\"\"", "abcxyz\"\"\"",       // 17..22
                "\"\"abcxyz\"", "\"\"abc\"xyz\"", "\"\"abc\"\"xyz\"", "\"\"abc\"\"\"xyz\"",               // 23..26
                "\"abcxyz", "\"\"\"abc\"xyz\"", "\"abc\"xyz", "\"\"\"abc\"\"\"xyz\"",                     // 27..30
                "\"abcxyz\"\"", "\"abc\"xyz\"\"", "\"abc\"\"xyz\"\"", "\"abc\"\"\"xyz\"\"",               // 31..34
                "abcxyz\"", "\"abc\"xyz\"\"\"", "abc\"xyz\"", "\"abc\"\"\"xyz\"\"\""                      // 35..38
        };

        // Identifiers quoted in non-pedantic mode
        String[] identifiersQuotedNonPedantic = new String[] { "\"abcxyz\"", "\"abc\"\"xyz\"", "\"abc\"\"\"\"xyz\"", "\"abc\"\"\"\"\"\"xyz\"",      // 1..4
                "\"abcxyz\"", "\"\"\"abc\"\"xyz\"\"\"", "\"abc\"\"xyz\"", "\"\"\"abc\"\"\"\"\"\"xyz\"\"\"",                                         // 5..8
                "\"\"\"\"\"abcxyz\"\"\"\"\"", "\"\"\"\"\"abc\"\"xyz\"\"\"\"\"",                                                                     // 9..
                "\"\"\"\"\"abc\"\"\"\"xyz\"\"\"\"\"", "\"\"\"\"\"abc\"\"\"\"\"\"xyz\"\"\"\"\"",                                                     //  ..12
                "\"\"\"abcxyz\"\"\"", "\"\"\"\"\"\"\"abc\"\"xyz\"\"\"\"\"\"\"",                                                                     // 13..
                "\"\"\"abc\"\"xyz\"\"\"", "\"\"\"\"\"\"\"abc\"\"\"\"\"\"xyz\"\"\"\"\"\"\"",                                                         //   ..16
                "\"\"\"abcxyz\"", "\"\"\"\"\"abcxyz\"", "\"\"\"\"\"\"\"abcxyz\"", "\"abcxyz\"\"\"", "\"abcxyz\"\"\"\"\"", "\"abcxyz\"\"\"\"\"\"\"", // 17..22
                "\"\"\"\"\"abcxyz\"\"\"", "\"\"\"\"\"abc\"\"xyz\"\"\"", "\"\"\"\"\"abc\"\"\"\"xyz\"\"\"", "\"\"\"\"\"abc\"\"\"\"\"\"xyz\"\"\"",     // 23..26
                "\"\"\"abcxyz\"", "\"\"\"\"\"\"\"abc\"\"xyz\"\"\"", "\"\"\"abc\"\"xyz\"", "\"\"\"\"\"\"\"abc\"\"\"\"\"\"xyz\"\"\"",                 // 27..30
                "\"\"\"abcxyz\"\"\"\"\"", "\"\"\"abc\"\"xyz\"\"\"\"\"", "\"\"\"abc\"\"\"\"xyz\"\"\"\"\"", "\"\"\"abc\"\"\"\"\"\"xyz\"\"\"\"\"",     // 31..34
                "\"abcxyz\"\"\"", "\"\"\"abc\"\"xyz\"\"\"\"\"\"\"", "\"abc\"\"xyz\"\"\"", "\"\"\"abc\"\"\"\"\"\"xyz\"\"\"\"\"\"\""                  // 35..38
        };

        // Identifiers quoted in pedantic mode
        String[] identifiersQuotedPedantic = new String[] { "\"abcxyz\"", "\"abc\"\"xyz\"", "\"abc\"\"\"\"xyz\"", "\"abc\"\"\"\"\"\"xyz\"",         // 1..4
                "\"\"\"abcxyz\"\"\"", "\"\"\"abc\"\"xyz\"\"\"", "\"\"\"abc\"\"\"\"xyz\"\"\"", "\"\"\"abc\"\"\"\"\"\"xyz\"\"\"",                     // 5..8
                "\"\"\"\"\"abcxyz\"\"\"\"\"", "\"\"\"\"\"abc\"\"xyz\"\"\"\"\"",                                                                     // 9..
                "\"\"\"\"\"abc\"\"\"\"xyz\"\"\"\"\"", "\"\"\"\"\"abc\"\"\"\"\"\"xyz\"\"\"\"\"",                                                     //  ..12
                "\"\"\"\"\"\"\"abcxyz\"\"\"\"\"\"\"", "\"\"\"\"\"\"\"abc\"\"xyz\"\"\"\"\"\"\"",                                                     // 13..
                "\"\"\"\"\"\"\"abc\"\"\"\"xyz\"\"\"\"\"\"\"", "\"\"\"\"\"\"\"abc\"\"\"\"\"\"xyz\"\"\"\"\"\"\"",                                     //   ..16
                "\"\"\"abcxyz\"", "\"\"\"\"\"abcxyz\"", "\"\"\"\"\"\"\"abcxyz\"", "\"abcxyz\"\"\"", "\"abcxyz\"\"\"\"\"", "\"abcxyz\"\"\"\"\"\"\"", // 17..22
                "\"\"\"\"\"abcxyz\"\"\"", "\"\"\"\"\"abc\"\"xyz\"\"\"", "\"\"\"\"\"abc\"\"\"\"xyz\"\"\"", "\"\"\"\"\"abc\"\"\"\"\"\"xyz\"\"\"",     // 23..26
                "\"\"\"\"\"\"\"abcxyz\"\"\"", "\"\"\"\"\"\"\"abc\"\"xyz\"\"\"",                                                                     // 27..
                "\"\"\"\"\"\"\"abc\"\"\"\"xyz\"\"\"", "\"\"\"\"\"\"\"abc\"\"\"\"\"\"xyz\"\"\"",                                                     //   ..30
                "\"\"\"abcxyz\"\"\"\"\"", "\"\"\"abc\"\"xyz\"\"\"\"\"", "\"\"\"abc\"\"\"\"xyz\"\"\"\"\"", "\"\"\"abc\"\"\"\"\"\"xyz\"\"\"\"\"",     // 31..34
                "\"\"\"abcxyz\"\"\"\"\"\"\"", "\"\"\"abc\"\"xyz\"\"\"\"\"\"\"",                                                                     // 35..
                "\"\"\"abc\"\"\"\"xyz\"\"\"\"\"\"\"", "\"\"\"abc\"\"\"\"\"\"xyz\"\"\"\"\"\"\""                                                      //   ..38
        };

        // Quoting rules (non-pedantic mode):
        // * identifiers[n] --> identifiersQuotedNonPedantic[n]
        for (int i = 0; i < identifiers.length; i++) {
            assertEquals(identifiersQuotedNonPedantic[i], StringUtils.quoteIdentifier(identifiers[i], '"', false),
                    i + 1 + ". " + identifiers[i] + ". non-pedantic quoting");
        }

        // Quoting rules (pedantic mode):
        // * identifiers[n] --> identifiersQuotedPedantic[n]
        // * identifiersUnQuoted[n] --> identifiersQuotedNonPedantic[n]
        for (int i = 0; i < identifiers.length; i++) {
            assertEquals(identifiersQuotedPedantic[i], StringUtils.quoteIdentifier(identifiers[i], '"', true),
                    i + 1 + ". " + identifiers[i] + ". pedantic quoting");

            assertEquals(identifiersQuotedNonPedantic[i], StringUtils.quoteIdentifier(identifiersUnQuoted[i], '"', true),
                    i + 1 + ". " + identifiersUnQuoted[i] + ". pedantic quoting");
        }

        // Unquoting rules:
        // * identifiers[n] --> identifiersUnQuoted[n]
        // * identifiersQuotedNonPedantic[n] --> identifiersUnQuoted[n]
        // * identifiersQuotedPedantic[n] --> identifiers[n]
        for (int i = 0; i < identifiers.length; i++) {
            assertEquals(identifiersUnQuoted[i], StringUtils.unquoteIdentifier(identifiers[i], '"'), i + 1 + ". " + identifiers[i] + ". unquoting");
            assertEquals(identifiersUnQuoted[i], StringUtils.unquoteIdentifier(identifiersQuotedNonPedantic[i], '"'),
                    i + 1 + ". " + identifiersQuotedNonPedantic[i] + ". non-pedantic unquoting");
            assertEquals(identifiers[i], StringUtils.unquoteIdentifier(identifiersQuotedPedantic[i], '"'),
                    i + 1 + ". " + identifiersQuotedPedantic[i] + ". pedantic unquoting");
        }
    }

    /**
     * Tests StringUtils.wildCompare() method.
     *
     * @throws Exception
     */
    @Test
    public void testWildCompare() throws Exception {
        // Null values.
        assertFalse(StringUtils.wildCompareIgnoreCase(null, null));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", null));
        assertFalse(StringUtils.wildCompareIgnoreCase(null, "abcxyz"));
        assertFalse(StringUtils.wildCompareIgnoreCase(null, "_"));
        assertFalse(StringUtils.wildCompareIgnoreCase(null, "%"));
        assertFalse(StringUtils.wildCompareIgnoreCase(null, "_%"));
        assertFalse(StringUtils.wildCompareIgnoreCase(null, "%_"));

        // Empty values.
        assertTrue(StringUtils.wildCompareIgnoreCase("", ""));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", ""));
        assertFalse(StringUtils.wildCompareIgnoreCase("", "abcxyz"));

        // Different letter case.
        assertTrue(StringUtils.wildCompareIgnoreCase("ABCxyz", "abcxyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcXYZ", "abcxyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("ABCXYZ", "abcxyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "ABCxyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abcXYZ"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "ABCXYZ"));
        assertTrue(StringUtils.wildCompareIgnoreCase("ABCxyz", "ab%YZ"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcXYZ", "AB%yz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("ABCxyz", "ab__YZ"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcXYZ", "AB__yz"));

        // Patterns without wildcards.
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abcxyz"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "zyxcba"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "a"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "abcxy"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "z"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "bcxyz"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "aabcxyz"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "abcxyzz"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "abclmnxyz"));

        // Patterns with wildcard %.
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "a%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "a%%%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc%%%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abcxyz%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abcxyz%%%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%z"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%z"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%abcxyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%abcxyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "a%z"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "a%%%z"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc%xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc%%%xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%cx%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%cx%%%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%abcxyz%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%abcxyz%%%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%b%y%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%b%%%y%%%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%a%b%c%x%y%z%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%a%%%b%%%c%%%x%%%y%%%z%%%"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "abcxyz%z"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "abcxyz%%%z"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "a%abcxyz"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "a%%%abcxyz"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "ab%cx%cx%yz"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "ab%%%cx%%%cx%%%yz"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "abc%x"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "abc%%%x"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "c%xyz"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "c%%%xyz"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%a%m%z%"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%a%%%m%%%z%%%"));

        // Patterns with wildcard _.
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abcxy_"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc___"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "a_____"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_bcxyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "___xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_____z"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "ab__yz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "a____z"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_bcxy_"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "__cx__"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "a_c_y_"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_b_x_z"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "______"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "abcxyz_"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "abc____"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "a______"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "_abcxyz"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "____xyz"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "______z"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "abc_xyz"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "ab___yz"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "a_____z"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "_bc_xy_"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "__c_x__"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "a_c_y__"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "__b_x_z"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "_______"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "abcx_"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "abc__"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "a____"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "_cxyz"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "__xyz"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "____z"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "ab_yz"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "a___z"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "_cxy_"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "a_c__"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "__x_z"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "_____"));

        // Patterns with both wildcards.
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc_%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc_%%%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc___%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc___%%%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc%_"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc%%%_"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc%___"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "abc%%%___"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%_xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%_xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%___xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%___xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_%xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_%%%_xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "___%xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "___%%%xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%_cx_%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%_cx_%%%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%__cx__%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%__cx__%%%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_%cx%_"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_%%%cx%%%_"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "__%cx%__"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "__%%%cx%%%__"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_b%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_b%%%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_____z%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_____z%%%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%y_"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%y_"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%a_____"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%a_____"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%abc%_%_%_%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%abc%%%_%%%_%%%_%%%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%_%_%_%xyz%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%_%%%_%%%_%%%xyz%%%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%a%_%c%x%_%z%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%a%%%_%%%c%%%x%%%_%%%z%%%"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%_c%m%x_%"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%%_c%%%m%%%x_%%"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "a%a_c%_yz"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "a%%%a_c%%%_yz"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "ab_%x_z%z"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "ab_%%%x_z%%%Z"));

        // Patterns with wildcards only.
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "_%%%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%_"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%_"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%_%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%_%%%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%___%___%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%___%%%___%%%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%_%_%_%_%_%_%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%_%%%_%%%_%%%_%%%_%%%_%%%"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%___%_%___%"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%%%___%%%_%%%___%%%"));

        // Escaped wildcards
        assertTrue(StringUtils.wildCompareIgnoreCase("abc%", "abc%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abc%%%", "abc%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abc%", "abc_"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abc%%%", "abc___"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abc%", "abc\\%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abc%%%", "abc\\%\\%\\%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abc%", "abc%\\%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abc%%%", "abc%\\%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abc%", "abc\\%%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abc%%%", "abc\\%%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abc_", "abc%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abc___", "abc%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abc_", "abc_"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abc___", "abc___"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abc_", "abc\\_"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abc___", "abc\\_\\_\\_"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abc___", "abc\\_\\__"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abc___", "abc\\__\\_"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abc___", "abc_\\_\\_"));
        assertTrue(StringUtils.wildCompareIgnoreCase("%xyz", "%xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("%%%xyz", "%xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("%xyz", "_xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("%%%xyz", "___xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("%xyz", "\\%xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("%%%xyz", "\\%\\%\\%xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("%xyz", "%\\%xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("%%%xyz", "%\\%xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("%xyz", "\\%%xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("%%%xyz", "\\%%xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("_xyz", "%xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("___xyz", "%xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("_xyz", "_xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("___xyz", "___xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("_xyz", "\\_xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("___xyz", "\\_\\_\\_xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("___xyz", "\\_\\__xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("___xyz", "\\__\\_xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("___xyz", "_\\_\\_xyz"));
        assertTrue(StringUtils.wildCompareIgnoreCase("%_%", "\\%\\_\\%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("_%_", "\\_\\%\\_"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%abc\\%"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%abc%\\%%"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "\\%xyz%"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%\\%%xyz%"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%abc\\%xyz%"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%abc%\\%%xyz%"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%abc\\_%"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%abc%\\_%"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%\\_xyz%"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%\\_%xyz%"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%abc\\_xyz%"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyz", "%abc%\\_%xyz%"));

        // Values with repeated patterns.
        assertTrue(StringUtils.wildCompareIgnoreCase("abcabcabcabcabcabc", "%abc"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcabcabcabcabcabc", "%%%abc"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcabcabcabcabcabc", "abc%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcabcabcabcabcabc", "abc%%%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcabcabcabcabcabc", "%abc%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcabcabcabcabcabc", "%%%abc%%%"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcabcabcabcabcabc", "abc%abc"));
        assertTrue(StringUtils.wildCompareIgnoreCase("abcabcabcabcabcabc", "abc%%%abc"));
        assertFalse(StringUtils.wildCompareIgnoreCase("xyzxyzxyzxyzxyzabc", "%xyz"));
        assertFalse(StringUtils.wildCompareIgnoreCase("xyzxyzxyzxyzxyzabc", "%%%xyz"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyzxyzxyzxyzxyz", "xyz%"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyzxyzxyzxyzxyz", "xyz%%%"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcabcabcabcabcabc", "%xyz%"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcabcabcabcabcabc", "%%%xyz%%%"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyzxyzxyzxyzabc", "abc%xyz"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyzxyzxyzxyzabc", "abc%%%xyz"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyzxyzxyzxyzabc", "xyz%abc"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyzxyzxyzxyzabc", "xyz%%%abc"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyzxyzxyzxyzabc", "xyz%xyz"));
        assertFalse(StringUtils.wildCompareIgnoreCase("abcxyzxyzxyzxyzabc", "xyz%%%xyz"));
    }

    /**
     * Tests StringUtils.sanitizeProcOrFuncName() and StringUtils.splitDBdotName() methods.
     *
     * @throws Exception
     */
    @Test
    public void testSanitizeAndSplitDot() throws Exception {
        String src = null;
        String resString = null;
        List<String> results = new ArrayList<>();

        // Test 1.1, weird DB.SP name.
        src = "`MyDatabase 1.0.1.0`.`Proc 1.v1`";
        resString = StringUtils.sanitizeProcOrFuncName(src);
        assertNotNull(resString, "Test 1.1 returned null resString");
        results = StringUtils.splitDbDotName(resString, null, '`', true);
        assertEquals("`MyDatabase 1.0.1.0`", results.get(0));
        assertEquals("`Proc 1.v1`", results.get(1));

        // Test 1.2, toggle isNoBslashEscSet.
        src = "`MyDatabase 1.0.1.0`.`Proc 1.v1`";
        resString = StringUtils.sanitizeProcOrFuncName(src);
        assertNotNull(resString, "Test 1.2 returned null resString");
        results = StringUtils.splitDbDotName(resString, null, '`', false);
        assertEquals("`MyDatabase 1.0.1.0`", results.get(0));
        assertEquals("`Proc 1.v1`", results.get(1));

        // Test 2.1, weird SP name, no DB parameter.
        src = "`Proc 1.v1`";
        resString = StringUtils.sanitizeProcOrFuncName(src);
        assertNotNull(resString, "Test 2.1 returned null resString");
        results = StringUtils.splitDbDotName(resString, null, '`', true);
        assertNull(results.get(0));
        assertEquals("`Proc 1.v1`", results.get(1));

        // Test 2.2, toggle isNoBslashEscSet.
        src = "`Proc 1.v1`";
        resString = StringUtils.sanitizeProcOrFuncName(src);
        assertNotNull(resString, "Test 2.2 returned null resString");
        results = StringUtils.splitDbDotName(resString, null, '`', false);
        assertNull(results.get(0));
        assertEquals("`Proc 1.v1`", results.get(1));
    }

    /**
     * Tests StringUtils.split() methods.
     *
     * @throws Exception
     */
    @Test
    public void testSplit() throws Exception {
        String testString = "  abstract, (contents, table of \"['figure''s'],(tables\"),  introduction  , \"methods(), ()results\", ['discussion'']', conclusion]   ";
        List<String> stringParts;

        // non existing split char, trim
        stringParts = StringUtils.split(testString, ";", true);
        assertEquals(1, stringParts.size());
        assertEquals(testString.trim(), stringParts.get(0));

        // non existing split char, don't trim
        stringParts = StringUtils.split(testString, ";", false);
        assertEquals(1, stringParts.size());
        assertEquals(testString, stringParts.get(0));

        // full split, trim
        stringParts = StringUtils.split(testString, ",", true);
        assertEquals(9, stringParts.size());
        assertEquals("abstract", stringParts.get(0));
        assertEquals("(contents", stringParts.get(1));
        assertEquals("table of \"['figure''s']", stringParts.get(2));
        assertEquals("(tables\")", stringParts.get(3));
        assertEquals("introduction", stringParts.get(4));
        assertEquals("\"methods()", stringParts.get(5));
        assertEquals("()results\"", stringParts.get(6));
        assertEquals("['discussion'']'", stringParts.get(7));
        assertEquals("conclusion]", stringParts.get(8));

        // full split, don't trim
        stringParts = StringUtils.split(testString, ",", false);
        assertEquals(9, stringParts.size());
        assertEquals("  abstract", stringParts.get(0));
        assertEquals(" (contents", stringParts.get(1));
        assertEquals(" table of \"['figure''s']", stringParts.get(2));
        assertEquals("(tables\")", stringParts.get(3));
        assertEquals("  introduction  ", stringParts.get(4));
        assertEquals(" \"methods()", stringParts.get(5));
        assertEquals(" ()results\"", stringParts.get(6));
        assertEquals(" ['discussion'']'", stringParts.get(7));
        assertEquals(" conclusion]   ", stringParts.get(8));

        // most common markers, trim
        stringParts = StringUtils.split(testString, ",", "'\"", "'\"", true);
        assertEquals(7, stringParts.size());
        assertEquals("abstract", stringParts.get(0));
        assertEquals("(contents", stringParts.get(1));
        assertEquals("table of \"['figure''s'],(tables\")", stringParts.get(2));
        assertEquals("introduction", stringParts.get(3));
        assertEquals("\"methods(), ()results\"", stringParts.get(4));
        assertEquals("['discussion'']'", stringParts.get(5));
        assertEquals("conclusion]", stringParts.get(6));

        // extended markers, trim
        stringParts = StringUtils.split(testString, ",", "'\"([{", "'\")]}", true);
        assertEquals(2, stringParts.size());
        assertEquals("abstract", stringParts.get(0));
        assertEquals("(contents, table of \"['figure''s'],(tables\"),  introduction  , \"methods(), ()results\", ['discussion'']', conclusion]",
                stringParts.get(1));

        // extended markers with overridable markers, trim
        stringParts = StringUtils.split(testString, ",", "'\"([{", "'\")]}", "'\"", true);
        assertEquals(5, stringParts.size());
        assertEquals("abstract", stringParts.get(0));
        assertEquals("(contents, table of \"['figure''s'],(tables\")", stringParts.get(1));
        assertEquals("introduction", stringParts.get(2));
        assertEquals("\"methods(), ()results\"", stringParts.get(3));
        assertEquals("['discussion'']', conclusion]", stringParts.get(4));
    }

    /**
     * Tests StringUtils.split() methods for corner cases.
     *
     * @throws Exception
     */
    @Test
    public void testSplitCornerCases() throws Exception {
        List<String> stringParts;

        int c = 0;
        for (String s : new String[] { ",", "  ,", ",  ", "  ,  " }) {
            String testCase = "Case: >" + s + "<";

            // single delimiter, trim
            stringParts = StringUtils.split(s, ",", true);
            assertEquals(2, stringParts.size(), testCase);
            assertEquals("", stringParts.get(0), testCase);
            assertEquals("", stringParts.get(1), testCase);
            stringParts = StringUtils.split(s, ",", "([", ")]", true);
            assertEquals(2, stringParts.size(), testCase);
            assertEquals("", stringParts.get(0), testCase);
            assertEquals("", stringParts.get(1), testCase);

            // single delimiter, don't trim
            stringParts = StringUtils.split(s, ",", false);
            assertEquals(2, stringParts.size(), testCase);
            assertEquals((c & 0x01) == 0 ? "" : "  ", stringParts.get(0), testCase);
            assertEquals((c & 0x02) == 0 ? "" : "  ", stringParts.get(1), testCase);
            stringParts = StringUtils.split(s, ",", "([", ")]", false);
            assertEquals(2, stringParts.size(), testCase);
            assertEquals((c & 0x01) == 0 ? "" : "  ", stringParts.get(0), testCase);
            assertEquals((c & 0x02) == 0 ? "" : "  ", stringParts.get(1), testCase);
            c++;
        }

        // multiple delimiter (condensed)
        c = 0;
        for (String s : new String[] { ",,,", "  ,,,", ",,,  ", "  ,,,  " }) { // [empty|2sp], empty, empty, [empty|2sp]
            String testCase = "Case: >" + s + "<";

            // trim
            stringParts = StringUtils.split(s, ",", true);
            assertEquals(4, stringParts.size(), testCase);
            assertEquals("", stringParts.get(0), testCase);
            assertEquals("", stringParts.get(1), testCase);
            assertEquals("", stringParts.get(2), testCase);
            assertEquals("", stringParts.get(3), testCase);
            stringParts = StringUtils.split(s, ",", "([", ")]", true);
            assertEquals(4, stringParts.size(), testCase);
            assertEquals("", stringParts.get(0), testCase);
            assertEquals("", stringParts.get(1), testCase);
            assertEquals("", stringParts.get(2), testCase);
            assertEquals("", stringParts.get(3), testCase);

            // don't trim
            stringParts = StringUtils.split(s, ",", false);
            assertEquals(4, stringParts.size(), testCase);
            assertEquals((c & 0x01) == 0 ? "" : "  ", stringParts.get(0), testCase); // [empty|2sp]
            assertEquals("", stringParts.get(1), testCase); // empty
            assertEquals("", stringParts.get(2), testCase); // empty
            assertEquals((c & 0x02) == 0 ? "" : "  ", stringParts.get(3), testCase); // [empty|2sp]
            stringParts = StringUtils.split(s, ",", "([", ")]", false);
            assertEquals(4, stringParts.size(), testCase);
            assertEquals((c & 0x01) == 0 ? "" : "  ", stringParts.get(0), testCase); // [empty|2sp]
            assertEquals("", stringParts.get(1), testCase); // empty
            assertEquals("", stringParts.get(2), testCase); // empty
            assertEquals((c & 0x02) == 0 ? "" : "  ", stringParts.get(3), testCase); // [empty|2sp]
            c++;
        }

        // multiple delimiter (mixed)
        c = 0;
        for (String s : new String[] { ",, ,", "  ,, ,", ",, ,  ", "  ,, ,  " }) { // [empty|2sp], empty, 1sp, [empty|2sp]
            String testCase = "Case: >" + s + "<";

            // trim
            stringParts = StringUtils.split(s, ",", true);
            assertEquals(4, stringParts.size(), testCase);
            assertEquals("", stringParts.get(0), testCase);
            assertEquals("", stringParts.get(1), testCase);
            assertEquals("", stringParts.get(2), testCase);
            assertEquals("", stringParts.get(3), testCase);
            stringParts = StringUtils.split(s, ",", "([", ")]", true);
            assertEquals(4, stringParts.size(), testCase);
            assertEquals("", stringParts.get(0), testCase);
            assertEquals("", stringParts.get(1), testCase);
            assertEquals("", stringParts.get(2), testCase);
            assertEquals("", stringParts.get(3), testCase);

            // don't trim
            stringParts = StringUtils.split(s, ",", false);
            assertEquals(4, stringParts.size(), testCase);
            assertEquals((c & 0x01) == 0 ? "" : "  ", stringParts.get(0), testCase); // [empty|2sp]
            assertEquals("", stringParts.get(1), testCase); // empty
            assertEquals(" ", stringParts.get(2), testCase); // 1sp
            assertEquals((c & 0x02) == 0 ? "" : "  ", stringParts.get(3), testCase);// [empty|2sp]
            stringParts = StringUtils.split(s, ",", "([", ")]", false);
            assertEquals(4, stringParts.size(), testCase);
            assertEquals((c & 0x01) == 0 ? "" : "  ", stringParts.get(0), testCase); // [empty|2sp]
            assertEquals("", stringParts.get(1), testCase); // empty
            assertEquals(" ", stringParts.get(2), testCase); // 1sp
            assertEquals((c & 0x02) == 0 ? "" : "  ", stringParts.get(3), testCase);// [empty|2sp]
            c++;
        }

        // multiple delimiter (scattered)
        c = 0;
        for (String s : new String[] { ",   , ,", "  ,   , ,", ",   , ,  ", "  ,   , ,  " }) { // [empty|2sp], 3sp, 1sp, [empty|2sp]
            String testCase = "Case: >" + s + "<";

            // trim
            stringParts = StringUtils.split(s, ",", true);
            assertEquals(4, stringParts.size(), testCase);
            assertEquals("", stringParts.get(0), testCase);
            assertEquals("", stringParts.get(1), testCase);
            assertEquals("", stringParts.get(2), testCase);
            assertEquals("", stringParts.get(3), testCase);
            stringParts = StringUtils.split(s, ",", "([", ")]", true);
            assertEquals(4, stringParts.size(), testCase);
            assertEquals("", stringParts.get(0), testCase);
            assertEquals("", stringParts.get(1), testCase);
            assertEquals("", stringParts.get(2), testCase);
            assertEquals("", stringParts.get(3), testCase);

            // don't trim
            stringParts = StringUtils.split(s, ",", false);
            assertEquals(4, stringParts.size(), testCase);
            assertEquals((c & 0x01) == 0 ? "" : "  ", stringParts.get(0), testCase); // [empty|2sp]
            assertEquals("   ", stringParts.get(1), testCase); // 3sp
            assertEquals(" ", stringParts.get(2), testCase); // 1sp
            assertEquals((c & 0x02) == 0 ? "" : "  ", stringParts.get(3), testCase); // [empty|2sp]
            stringParts = StringUtils.split(s, ",", "([", ")]", false);
            assertEquals(4, stringParts.size(), testCase);
            assertEquals((c & 0x01) == 0 ? "" : "  ", stringParts.get(0), testCase); // [empty|2sp]
            assertEquals("   ", stringParts.get(1), testCase); // 3sp
            assertEquals(" ", stringParts.get(2), testCase); // 1sp
            assertEquals((c & 0x02) == 0 ? "" : "  ", stringParts.get(3), testCase); // [empty|2sp]
            c++;
        }
    }

    /**
     * Tests StringUtils.joinWithSerialComma().
     *
     * @throws Exception
     */
    @Test
    public void testJoinWithSerialComma() throws Exception {
        assertEquals("", StringUtils.joinWithSerialComma(null));
        assertEquals("", StringUtils.joinWithSerialComma(Collections.emptyList()));

        // Using lists of Strings
        assertEquals("A", StringUtils.joinWithSerialComma(Arrays.asList("A")));
        assertEquals("A and B", StringUtils.joinWithSerialComma(Arrays.asList("A", "B")));
        assertEquals("A, B, and C", StringUtils.joinWithSerialComma(Arrays.asList("A", "B", "C")));

        // Using lists of objects other than Strings
        assertEquals("A", StringUtils.joinWithSerialComma(Arrays.asList(new LazyString("A"))));
        assertEquals("A and B", StringUtils.joinWithSerialComma(Arrays.asList(new LazyString("A"), new LazyString("B"))));
        assertEquals("A, B, and C", StringUtils.joinWithSerialComma(Arrays.asList(new LazyString("A"), new LazyString("B"), new LazyString("C"))));
    }

    /**
     * Tests StringUtils.unquoteBytes().
     *
     * @throws Exception
     */
    @Test
    public void testQuoteUnquoteBytes() throws Exception {
        byte[] origBytes = "ab'c\\''de".getBytes();
        byte[] expectedBytes = "'ab''c\\''''de'".getBytes();
        byte[] quotedBytes = StringUtils.quoteBytes(origBytes);

        for (int i = 0; i < quotedBytes.length; i++) {
            assertEquals(expectedBytes[i], quotedBytes[i]);
        }

        byte[] unquotedBytes = StringUtils.unquoteBytes(quotedBytes);

        for (int i = 0; i < unquotedBytes.length; i++) {
            assertEquals(origBytes[i], unquotedBytes[i]);
        }
    }

    @Test
    public void testStripComments() throws Exception {
        String testString = "-- 1st comment\nthis\nis/* 2nd comment */not # 3rd \"comment\"\n-- 4th comment\r\n  /* 5th comment */a 'comment'"
                + "--neither this! nor '# this is a comment'";
        String expected = "this\nis not   a 'comment'--neither this! nor '# this is a comment'";

        assertEquals(expected, StringUtils.stripCommentsAndHints(testString, "\"'", "\"'", true));
    }

    /**
     * Tests StringUtils.enquoteLiteral().
     *
     * @throws Exception
     */
    @Test
    void testEnquoteLiteral() throws Exception {
        boolean pdn = false; // Pedantic.
        boolean asq = false; // Ansi-quotes.
        boolean bse = false; // Backslash escapes.

        do {
            String testCase = String.format("Case [ pedantic; %s, ansiQuotes: %s, bsEscape: %s ]", pdn ? "Y" : "N", asq ? "Y" : "N", bse ? "Y" : "N");

            // No quotes.
            assertEquals("''", StringUtils.enquoteLiteral("", pdn, asq, bse), testCase);
            assertEquals("' '", StringUtils.enquoteLiteral(" ", pdn, asq, bse), testCase);
            assertEquals("'abc'", StringUtils.enquoteLiteral("abc", pdn, asq, bse), testCase);
            assertEquals("'abc\ndef'", StringUtils.enquoteLiteral("abc\ndef", pdn, asq, bse), testCase);
            assertEquals(bse ? "'abc\\\\'" : "'abc\\'", StringUtils.enquoteLiteral("abc\\", pdn, asq, bse), testCase);
            assertEquals("'abc\\\\'", StringUtils.enquoteLiteral("abc\\\\", pdn, asq, bse), testCase);
            assertEquals(bse ? "'abc\\\\\\\\'" : "'abc\\\\\\'", StringUtils.enquoteLiteral("abc\\\\\\", pdn, asq, bse), testCase);

            // Single quote.
            assertEquals("''''", StringUtils.enquoteLiteral("'", pdn, asq, bse), testCase);
            assertEquals(bse ? "'\\''" : "'\\'''", StringUtils.enquoteLiteral("\\'", pdn, asq, bse), testCase);
            assertEquals("'\\\\'''", StringUtils.enquoteLiteral("\\\\'", pdn, asq, bse), testCase);
            assertEquals(pdn ? "''''''" : "''", StringUtils.enquoteLiteral("''", pdn, asq, bse), testCase);
            assertEquals(bse ? "'\\'abc'''" : "'\\''abc'''", StringUtils.enquoteLiteral("\\'abc'", pdn, asq, bse), testCase);
            assertEquals("'\\\\''abc'''", StringUtils.enquoteLiteral("\\\\'abc'", pdn, asq, bse), testCase);
            assertEquals(bse ? "'\\\\\\'abc'''" : "'\\\\\\''abc'''", StringUtils.enquoteLiteral("\\\\\\'abc'", pdn, asq, bse), testCase);
            assertEquals(bse ? "'''abc\\''" : pdn ? "'''abc\\'''" : "'abc\\'", StringUtils.enquoteLiteral("'abc\\'", pdn, asq, bse), testCase);
            assertEquals(pdn ? "'''abc\\\\'''" : "'abc\\\\'", StringUtils.enquoteLiteral("'abc\\\\'", pdn, asq, bse), testCase);
            assertEquals(bse ? "'''abc\\\\\\''" : pdn ? "'''abc\\\\\\'''" : "'abc\\\\\\'", StringUtils.enquoteLiteral("'abc\\\\\\'", pdn, asq, bse), testCase);
            assertEquals("'abc''def'", StringUtils.enquoteLiteral("abc'def", pdn, asq, bse), testCase);
            assertEquals(bse ? "'abc\\'def'" : "'abc\\''def'", StringUtils.enquoteLiteral("abc\\'def", pdn, asq, bse), testCase);
            assertEquals("'abc\\\\''def'", StringUtils.enquoteLiteral("abc\\\\'def", pdn, asq, bse), testCase);
            assertEquals("'abc''''def'", StringUtils.enquoteLiteral("abc''def", pdn, asq, bse), testCase);
            assertEquals(pdn ? "'''abc'''" : "'abc'", StringUtils.enquoteLiteral("'abc'", pdn, asq, bse), testCase);
            assertEquals(pdn ? "'''abc\ndef'''" : "'abc\ndef'", StringUtils.enquoteLiteral("'abc\ndef'", pdn, asq, bse), testCase);
            assertEquals("'''abc''def'''", StringUtils.enquoteLiteral("'abc'def'", pdn, asq, bse), testCase);
            assertEquals(bse ? pdn ? "'''abc\\'def'''" : "'abc\\'def'" : "'''abc\\''def'''", StringUtils.enquoteLiteral("'abc\\'def'", pdn, asq, bse),
                    testCase);
            assertEquals("'''abc\\\\''def'''", StringUtils.enquoteLiteral("'abc\\\\'def'", pdn, asq, bse), testCase);
            assertEquals(pdn ? "'''abc''''def'''" : "'abc''def'", StringUtils.enquoteLiteral("'abc''def'", pdn, asq, bse), testCase);
            assertEquals(bse ? "'abc''def\\'ghi'" : "'abc''def\\''ghi'", StringUtils.enquoteLiteral("abc'def\\'ghi", pdn, asq, bse), testCase);
            assertEquals("'abc''def\\\\''ghi'", StringUtils.enquoteLiteral("abc'def\\\\'ghi", pdn, asq, bse), testCase);
            assertEquals(bse ? "'abc''''def\\'ghi'" : "'abc''''def\\''ghi'", StringUtils.enquoteLiteral("abc''def\\'ghi", pdn, asq, bse), testCase);
            assertEquals("'abc''''def\\\\''ghi'", StringUtils.enquoteLiteral("abc''def\\\\'ghi", pdn, asq, bse), testCase);
            assertEquals("'''abc\"'", StringUtils.enquoteLiteral("'abc\"", pdn, asq, bse), testCase);
            assertEquals(pdn ? "'''abc\"def'''" : "'abc\"def'", StringUtils.enquoteLiteral("'abc\"def'", pdn, asq, bse), testCase);
            assertEquals("'''abc\"def''''ghi\"'", StringUtils.enquoteLiteral("'abc\"def''ghi\"", pdn, asq, bse), testCase);
            assertEquals("'''abc\"def''ghi'''", StringUtils.enquoteLiteral("'abc\"def'ghi'", pdn, asq, bse), testCase);
            assertEquals(pdn ? "'''abc\"def''''ghi'''" : "'abc\"def''ghi'", StringUtils.enquoteLiteral("'abc\"def''ghi'", pdn, asq, bse), testCase);

            // Double quote.
            assertEquals("'\"'", StringUtils.enquoteLiteral("\"", pdn, asq, bse), testCase);
            assertEquals("'\\\"'", StringUtils.enquoteLiteral("\\\"", pdn, asq, bse), testCase);
            assertEquals("'\\\\\"'", StringUtils.enquoteLiteral("\\\\\"", pdn, asq, bse), testCase);
            assertEquals(pdn || asq ? "'\"\"'" : "\"\"", StringUtils.enquoteLiteral("\"\"", pdn, asq, bse), testCase);
            assertEquals("'\\\"abc\"'", StringUtils.enquoteLiteral("\\\"abc\"", pdn, asq, bse), testCase);
            assertEquals("'\\\\\"abc\"'", StringUtils.enquoteLiteral("\\\\\"abc\"", pdn, asq, bse), testCase);
            assertEquals("'\\\\\\\"abc\"'", StringUtils.enquoteLiteral("\\\\\\\"abc\"", pdn, asq, bse), testCase);
            assertEquals(pdn || asq || bse ? "'\"abc\\\"'" : "\"abc\\\"", StringUtils.enquoteLiteral("\"abc\\\"", pdn, asq, bse), testCase);
            assertEquals(pdn || asq ? "'\"abc\\\\\"'" : "\"abc\\\\\"", StringUtils.enquoteLiteral("\"abc\\\\\"", pdn, asq, bse), testCase);
            assertEquals(pdn || asq || bse ? "'\"abc\\\\\\\"'" : "\"abc\\\\\\\"", StringUtils.enquoteLiteral("\"abc\\\\\\\"", pdn, asq, bse), testCase);
            assertEquals("'abc\"def'", StringUtils.enquoteLiteral("abc\"def", pdn, asq, bse), testCase);
            assertEquals("'abc\\\"def'", StringUtils.enquoteLiteral("abc\\\"def", pdn, asq, bse), testCase);
            assertEquals("'abc\\\\\"def'", StringUtils.enquoteLiteral("abc\\\\\"def", pdn, asq, bse), testCase);
            assertEquals("'abc\"\"def'", StringUtils.enquoteLiteral("abc\"\"def", pdn, asq, bse), testCase);
            assertEquals(pdn || asq ? "'\"abc\"'" : "\"abc\"", StringUtils.enquoteLiteral("\"abc\"", pdn, asq, bse), testCase);
            assertEquals(pdn || asq ? "'\"abc\ndef\"'" : "\"abc\ndef\"", StringUtils.enquoteLiteral("\"abc\ndef\"", pdn, asq, bse), testCase);
            assertEquals("'\"abc\"def\"'", StringUtils.enquoteLiteral("\"abc\"def\"", pdn, asq, bse), testCase);
            assertEquals(pdn || asq || !bse ? "'\"abc\\\"def\"'" : "\"abc\\\"def\"", StringUtils.enquoteLiteral("\"abc\\\"def\"", pdn, asq, bse), testCase);
            assertEquals("'\"abc\\\\\"def\"'", StringUtils.enquoteLiteral("\"abc\\\\\"def\"", pdn, asq, bse), testCase);
            assertEquals(pdn || asq ? "'\"abc\"\"def\"'" : "\"abc\"\"def\"", StringUtils.enquoteLiteral("\"abc\"\"def\"", pdn, asq, bse), testCase);
            assertEquals("'abc\"def\\\"ghi'", StringUtils.enquoteLiteral("abc\"def\\\"ghi", pdn, asq, bse), testCase);
            assertEquals("'abc\"def\\\\\"ghi'", StringUtils.enquoteLiteral("abc\"def\\\\\"ghi", pdn, asq, bse), testCase);
            assertEquals("'abc\"\"def\\\"ghi'", StringUtils.enquoteLiteral("abc\"\"def\\\"ghi", pdn, asq, bse), testCase);
            assertEquals("'abc\"\"def\\\\\"ghi'", StringUtils.enquoteLiteral("abc\"\"def\\\\\"ghi", pdn, asq, bse), testCase);
            assertEquals("'\"abc'''", StringUtils.enquoteLiteral("\"abc'", pdn, asq, bse), testCase);
            assertEquals(pdn || asq ? "'\"abc''def\"'" : "\"abc'def\"", StringUtils.enquoteLiteral("\"abc'def\"", pdn, asq, bse), testCase);
            assertEquals("'\"abc''def\"\"ghi'''", StringUtils.enquoteLiteral("\"abc'def\"\"ghi'", pdn, asq, bse), testCase);
            assertEquals("'\"abc''def\"ghi\"'", StringUtils.enquoteLiteral("\"abc'def\"ghi\"", pdn, asq, bse), testCase);
            assertEquals(pdn || asq ? "'\"abc''def\"\"ghi\"'" : "\"abc'def\"\"ghi\"", StringUtils.enquoteLiteral("\"abc'def\"\"ghi\"", pdn, asq, bse),
                    testCase);
        } while ((pdn = !pdn) || (asq = !asq) || (bse = !bse));
    }

    /**
     * Tests StringUtils.enquoteIdentifier() using the backquote char.
     *
     * @throws Exception
     */
    @Test
    void testEnquoteIdentifierUsingBackquote() throws Exception {
        boolean pdn = false; // Pedantic.
        boolean asq = false; // Ansi-quotes.
        boolean bse = false; // Backslash escapes.

        do {
            String testCase = String.format("Case [ pedantic; %s, ansiQuotes: %s, bsEscape: %s ]", pdn ? "Y" : "N", asq ? "Y" : "N", bse ? "Y" : "N");

            // No quotes.
            assertEquals("``", StringUtils.enquoteIdentifier("", '`', pdn, asq, bse), testCase);
            assertEquals("` `", StringUtils.enquoteIdentifier(" ", '`', pdn, asq, bse), testCase);
            assertEquals("`abc`", StringUtils.enquoteIdentifier("abc", '`', pdn, asq, bse), testCase);
            assertEquals("`abc\ndef`", StringUtils.enquoteIdentifier("abc\ndef", '`', pdn, asq, bse), testCase);
            assertEquals(bse ? "`abc\\\\`" : "`abc\\`", StringUtils.enquoteIdentifier("abc\\", '`', pdn, asq, bse), testCase);
            assertEquals("`abc\\\\`", StringUtils.enquoteIdentifier("abc\\\\", '`', pdn, asq, bse), testCase);
            assertEquals(bse ? "`abc\\\\\\\\`" : "`abc\\\\\\`", StringUtils.enquoteIdentifier("abc\\\\\\", '`', pdn, asq, bse), testCase);

            // Backquote.
            assertEquals("````", StringUtils.enquoteIdentifier("`", '`', pdn, asq, bse), testCase);
            assertEquals(bse ? "`\\``" : "`\\```", StringUtils.enquoteIdentifier("\\`", '`', pdn, asq, bse), testCase);
            assertEquals("`\\\\```", StringUtils.enquoteIdentifier("\\\\`", '`', pdn, asq, bse), testCase);
            assertEquals(pdn ? "``````" : "``", StringUtils.enquoteIdentifier("``", '`', pdn, asq, bse), testCase);
            assertEquals(bse ? "`\\`abc```" : "`\\``abc```", StringUtils.enquoteIdentifier("\\`abc`", '`', pdn, asq, bse), testCase);
            assertEquals("`\\\\``abc```", StringUtils.enquoteIdentifier("\\\\`abc`", '`', pdn, asq, bse), testCase);
            assertEquals(bse ? "`\\\\\\`abc```" : "`\\\\\\``abc```", StringUtils.enquoteIdentifier("\\\\\\`abc`", '`', pdn, asq, bse), testCase);
            assertEquals(bse ? "```abc\\``" : pdn ? "```abc\\```" : "`abc\\`", StringUtils.enquoteIdentifier("`abc\\`", '`', pdn, asq, bse), testCase);
            assertEquals(pdn ? "```abc\\\\```" : "`abc\\\\`", StringUtils.enquoteIdentifier("`abc\\\\`", '`', pdn, asq, bse), testCase);
            assertEquals(bse ? "```abc\\\\\\``" : pdn ? "```abc\\\\\\```" : "`abc\\\\\\`", StringUtils.enquoteIdentifier("`abc\\\\\\`", '`', pdn, asq, bse),
                    testCase);
            assertEquals("`abc``def`", StringUtils.enquoteIdentifier("abc`def", '`', pdn, asq, bse), testCase);
            assertEquals(bse ? "`abc\\`def`" : "`abc\\``def`", StringUtils.enquoteIdentifier("abc\\`def", '`', pdn, asq, bse), testCase);
            assertEquals("`abc\\\\``def`", StringUtils.enquoteIdentifier("abc\\\\`def", '`', pdn, asq, bse), testCase);
            assertEquals("`abc````def`", StringUtils.enquoteIdentifier("abc``def", '`', pdn, asq, bse), testCase);
            assertEquals(pdn ? "```abc```" : "`abc`", StringUtils.enquoteIdentifier("`abc`", '`', pdn, asq, bse), testCase);
            assertEquals(pdn ? "```abc\ndef```" : "`abc\ndef`", StringUtils.enquoteIdentifier("`abc\ndef`", '`', pdn, asq, bse), testCase);
            assertEquals("```abc``def```", StringUtils.enquoteIdentifier("`abc`def`", '`', pdn, asq, bse), testCase);
            assertEquals(bse ? pdn ? "```abc\\`def```" : "`abc\\`def`" : "```abc\\``def```", StringUtils.enquoteIdentifier("`abc\\`def`", '`', pdn, asq, bse),
                    testCase);
            assertEquals("```abc\\\\``def```", StringUtils.enquoteIdentifier("`abc\\\\`def`", '`', pdn, asq, bse), testCase);
            assertEquals(pdn ? "```abc````def```" : "`abc``def`", StringUtils.enquoteIdentifier("`abc``def`", '`', pdn, asq, bse), testCase);
            assertEquals(bse ? "`abc``def\\`ghi`" : "`abc``def\\``ghi`", StringUtils.enquoteIdentifier("abc`def\\`ghi", '`', pdn, asq, bse), testCase);
            assertEquals("`abc``def\\\\``ghi`", StringUtils.enquoteIdentifier("abc`def\\\\`ghi", '`', pdn, asq, bse), testCase);
            assertEquals(bse ? "`abc````def\\`ghi`" : "`abc````def\\``ghi`", StringUtils.enquoteIdentifier("abc``def\\`ghi", '`', pdn, asq, bse), testCase);
            assertEquals("`abc````def\\\\``ghi`", StringUtils.enquoteIdentifier("abc``def\\\\`ghi", '`', pdn, asq, bse), testCase);
            assertEquals("```abc\"`", StringUtils.enquoteIdentifier("`abc\"", '`', pdn, asq, bse), testCase);
            assertEquals(pdn ? "```abc\"def```" : "`abc\"def`", StringUtils.enquoteIdentifier("`abc\"def`", '`', pdn, asq, bse), testCase);
            assertEquals("```abc\"def````ghi\"`", StringUtils.enquoteIdentifier("`abc\"def``ghi\"", '`', pdn, asq, bse), testCase);
            assertEquals("```abc\"def``ghi```", StringUtils.enquoteIdentifier("`abc\"def`ghi`", '`', pdn, asq, bse), testCase);
            assertEquals(pdn ? "```abc\"def````ghi```" : "`abc\"def``ghi`", StringUtils.enquoteIdentifier("`abc\"def``ghi`", '`', pdn, asq, bse), testCase);

            // Double quote.
            assertEquals("`\"`", StringUtils.enquoteIdentifier("\"", '`', pdn, asq, bse), testCase);
            assertEquals("`\\\"`", StringUtils.enquoteIdentifier("\\\"", '`', pdn, asq, bse), testCase);
            assertEquals("`\\\\\"`", StringUtils.enquoteIdentifier("\\\\\"", '`', pdn, asq, bse), testCase);
            assertEquals(pdn || !asq ? "`\"\"`" : "\"\"", StringUtils.enquoteIdentifier("\"\"", '`', pdn, asq, bse), testCase);
            assertEquals("`\\\"abc\"`", StringUtils.enquoteIdentifier("\\\"abc\"", '`', pdn, asq, bse), testCase);
            assertEquals("`\\\\\"abc\"`", StringUtils.enquoteIdentifier("\\\\\"abc\"", '`', pdn, asq, bse), testCase);
            assertEquals("`\\\\\\\"abc\"`", StringUtils.enquoteIdentifier("\\\\\\\"abc\"", '`', pdn, asq, bse), testCase);
            assertEquals(pdn || !asq || bse ? "`\"abc\\\"`" : "\"abc\\\"", StringUtils.enquoteIdentifier("\"abc\\\"", '`', pdn, asq, bse), testCase);
            assertEquals(pdn || !asq ? "`\"abc\\\\\"`" : "\"abc\\\\\"", StringUtils.enquoteIdentifier("\"abc\\\\\"", '`', pdn, asq, bse), testCase);
            assertEquals(pdn || !asq || bse ? "`\"abc\\\\\\\"`" : "\"abc\\\\\\\"", StringUtils.enquoteIdentifier("\"abc\\\\\\\"", '`', pdn, asq, bse),
                    testCase);
            assertEquals("`abc\"def`", StringUtils.enquoteIdentifier("abc\"def", '`', pdn, asq, bse), testCase);
            assertEquals("`abc\\\"def`", StringUtils.enquoteIdentifier("abc\\\"def", '`', pdn, asq, bse), testCase);
            assertEquals("`abc\\\\\"def`", StringUtils.enquoteIdentifier("abc\\\\\"def", '`', pdn, asq, bse), testCase);
            assertEquals("`abc\"\"def`", StringUtils.enquoteIdentifier("abc\"\"def", '`', pdn, asq, bse), testCase);
            assertEquals(pdn || !asq ? "`\"abc\"`" : "\"abc\"", StringUtils.enquoteIdentifier("\"abc\"", '`', pdn, asq, bse), testCase);
            assertEquals(pdn || !asq ? "`\"abc\ndef\"`" : "\"abc\ndef\"", StringUtils.enquoteIdentifier("\"abc\ndef\"", '`', pdn, asq, bse), testCase);
            assertEquals("`\"abc\"def\"`", StringUtils.enquoteIdentifier("\"abc\"def\"", '`', pdn, asq, bse), testCase);
            assertEquals(pdn || !asq || !bse ? "`\"abc\\\"def\"`" : "\"abc\\\"def\"", StringUtils.enquoteIdentifier("\"abc\\\"def\"", '`', pdn, asq, bse),
                    testCase);
            assertEquals("`\"abc\\\\\"def\"`", StringUtils.enquoteIdentifier("\"abc\\\\\"def\"", '`', pdn, asq, bse), testCase);
            assertEquals(pdn || !asq ? "`\"abc\"\"def\"`" : "\"abc\"\"def\"", StringUtils.enquoteIdentifier("\"abc\"\"def\"", '`', pdn, asq, bse), testCase);
            assertEquals("`abc\"def\\\"ghi`", StringUtils.enquoteIdentifier("abc\"def\\\"ghi", '`', pdn, asq, bse), testCase);
            assertEquals("`abc\"def\\\\\"ghi`", StringUtils.enquoteIdentifier("abc\"def\\\\\"ghi", '`', pdn, asq, bse), testCase);
            assertEquals("`abc\"\"def\\\"ghi`", StringUtils.enquoteIdentifier("abc\"\"def\\\"ghi", '`', pdn, asq, bse), testCase);
            assertEquals("`abc\"\"def\\\\\"ghi`", StringUtils.enquoteIdentifier("abc\"\"def\\\\\"ghi", '`', pdn, asq, bse), testCase);
            assertEquals("`\"abc```", StringUtils.enquoteIdentifier("\"abc`", '`', pdn, asq, bse), testCase);
            assertEquals(pdn || !asq ? "`\"abc``def\"`" : "\"abc`def\"", StringUtils.enquoteIdentifier("\"abc`def\"", '`', pdn, asq, bse), testCase);
            assertEquals("`\"abc``def\"\"ghi```", StringUtils.enquoteIdentifier("\"abc`def\"\"ghi`", '`', pdn, asq, bse), testCase);
            assertEquals("`\"abc``def\"ghi\"`", StringUtils.enquoteIdentifier("\"abc`def\"ghi\"", '`', pdn, asq, bse), testCase);
            assertEquals(pdn || !asq ? "`\"abc``def\"\"ghi\"`" : "\"abc`def\"\"ghi\"", StringUtils.enquoteIdentifier("\"abc`def\"\"ghi\"", '`', pdn, asq, bse),
                    testCase);
        } while ((pdn = !pdn) || (asq = !asq) || (bse = !bse));
    }

    /**
     * Tests StringUtils.enquoteIdentifier() using the double quote char.
     *
     * @throws Exception
     */
    @Test
    void testEnquoteIdentifierUsingDoubleQuote() throws Exception {
        boolean pdn = false; // Pedantic.
        boolean asq = false; // Ansi-quotes.
        boolean bse = false; // Backslash escapes.

        do {
            String testCase = String.format("Case [ pedantic; %s, ansiQuotes: %s, bsEscape: %s ]", pdn ? "Y" : "N", asq ? "Y" : "N", bse ? "Y" : "N");

            // No quotes.
            assertEquals("\"\"", StringUtils.enquoteIdentifier("", '\"', pdn, asq, bse), testCase);
            assertEquals("\" \"", StringUtils.enquoteIdentifier(" ", '\"', pdn, asq, bse), testCase);
            assertEquals("\"abc\"", StringUtils.enquoteIdentifier("abc", '\"', pdn, asq, bse), testCase);
            assertEquals("\"abc\ndef\"", StringUtils.enquoteIdentifier("abc\ndef", '\"', pdn, asq, bse), testCase);
            assertEquals(bse ? "\"abc\\\\\"" : "\"abc\\\"", StringUtils.enquoteIdentifier("abc\\", '\"', pdn, asq, bse), testCase);
            assertEquals("\"abc\\\\\"", StringUtils.enquoteIdentifier("abc\\\\", '\"', pdn, asq, bse), testCase);
            assertEquals(bse ? "\"abc\\\\\\\\\"" : "\"abc\\\\\\\"", StringUtils.enquoteIdentifier("abc\\\\\\", '\"', pdn, asq, bse), testCase);

            // Backquote.
            assertEquals("\"`\"", StringUtils.enquoteIdentifier("`", '\"', pdn, asq, bse), testCase);
            assertEquals("\"\\`\"", StringUtils.enquoteIdentifier("\\`", '\"', pdn, asq, bse), testCase);
            assertEquals("\"\\\\`\"", StringUtils.enquoteIdentifier("\\\\`", '\"', pdn, asq, bse), testCase);
            assertEquals(pdn ? "\"``\"" : "``", StringUtils.enquoteIdentifier("``", '\"', pdn, asq, bse), testCase);
            assertEquals("\"\\`abc`\"", StringUtils.enquoteIdentifier("\\`abc`", '\"', pdn, asq, bse), testCase);
            assertEquals("\"\\\\`abc`\"", StringUtils.enquoteIdentifier("\\\\`abc`", '\"', pdn, asq, bse), testCase);
            assertEquals("\"\\\\\\`abc`\"", StringUtils.enquoteIdentifier("\\\\\\`abc`", '\"', pdn, asq, bse), testCase);
            assertEquals(pdn || bse ? "\"`abc\\`\"" : "`abc\\`", StringUtils.enquoteIdentifier("`abc\\`", '\"', pdn, asq, bse), testCase);
            assertEquals(pdn ? "\"`abc\\\\`\"" : "`abc\\\\`", StringUtils.enquoteIdentifier("`abc\\\\`", '\"', pdn, asq, bse), testCase);
            assertEquals(pdn || bse ? "\"`abc\\\\\\`\"" : "`abc\\\\\\`", StringUtils.enquoteIdentifier("`abc\\\\\\`", '\"', pdn, asq, bse), testCase);
            assertEquals("\"abc`def\"", StringUtils.enquoteIdentifier("abc`def", '\"', pdn, asq, bse), testCase);
            assertEquals("\"abc\\`def\"", StringUtils.enquoteIdentifier("abc\\`def", '\"', pdn, asq, bse), testCase);
            assertEquals("\"abc\\\\`def\"", StringUtils.enquoteIdentifier("abc\\\\`def", '\"', pdn, asq, bse), testCase);
            assertEquals("\"abc``def\"", StringUtils.enquoteIdentifier("abc``def", '\"', pdn, asq, bse), testCase);
            assertEquals(pdn ? "\"`abc`\"" : "`abc`", StringUtils.enquoteIdentifier("`abc`", '\"', pdn, asq, bse), testCase);
            assertEquals(pdn ? "\"`abc\ndef`\"" : "`abc\ndef`", StringUtils.enquoteIdentifier("`abc\ndef`", '\"', pdn, asq, bse), testCase);
            assertEquals("\"`abc`def`\"", StringUtils.enquoteIdentifier("`abc`def`", '\"', pdn, asq, bse), testCase);
            assertEquals(pdn || !bse ? "\"`abc\\`def`\"" : "`abc\\`def`", StringUtils.enquoteIdentifier("`abc\\`def`", '\"', pdn, asq, bse), testCase);
            assertEquals("\"`abc\\\\`def`\"", StringUtils.enquoteIdentifier("`abc\\\\`def`", '\"', pdn, asq, bse), testCase);
            assertEquals(pdn ? "\"`abc``def`\"" : "`abc``def`", StringUtils.enquoteIdentifier("`abc``def`", '\"', pdn, asq, bse), testCase);
            assertEquals("\"abc`def\\`ghi\"", StringUtils.enquoteIdentifier("abc`def\\`ghi", '\"', pdn, asq, bse), testCase);
            assertEquals("\"abc`def\\\\`ghi\"", StringUtils.enquoteIdentifier("abc`def\\\\`ghi", '\"', pdn, asq, bse), testCase);
            assertEquals("\"abc``def\\`ghi\"", StringUtils.enquoteIdentifier("abc``def\\`ghi", '\"', pdn, asq, bse), testCase);
            assertEquals("\"abc``def\\\\`ghi\"", StringUtils.enquoteIdentifier("abc``def\\\\`ghi", '\"', pdn, asq, bse), testCase);
            assertEquals("\"`abc\"\"\"", StringUtils.enquoteIdentifier("`abc\"", '\"', pdn, asq, bse), testCase);
            assertEquals(pdn ? "\"`abc\"\"def`\"" : "`abc\"def`", StringUtils.enquoteIdentifier("`abc\"def`", '\"', pdn, asq, bse), testCase);
            assertEquals("\"`abc\"\"def``ghi\"\"\"", StringUtils.enquoteIdentifier("`abc\"def``ghi\"", '\"', pdn, asq, bse), testCase);
            assertEquals("\"`abc\"\"def`ghi`\"", StringUtils.enquoteIdentifier("`abc\"def`ghi`", '\"', pdn, asq, bse), testCase);
            assertEquals(pdn ? "\"`abc\"\"def``ghi`\"" : "`abc\"def``ghi`", StringUtils.enquoteIdentifier("`abc\"def``ghi`", '\"', pdn, asq, bse), testCase);

            // Double quote.
            assertEquals("\"\"\"\"", StringUtils.enquoteIdentifier("\"", '\"', pdn, asq, bse), testCase);
            assertEquals(bse ? "\"\\\"\"" : "\"\\\"\"\"", StringUtils.enquoteIdentifier("\\\"", '\"', pdn, asq, bse), testCase);
            assertEquals("\"\\\\\"\"\"", StringUtils.enquoteIdentifier("\\\\\"", '\"', pdn, asq, bse), testCase);
            assertEquals(pdn || !asq ? "\"\"\"\"\"\"" : "\"\"", StringUtils.enquoteIdentifier("\"\"", '\"', pdn, asq, bse), testCase);
            assertEquals(bse ? "\"\\\"abc\"\"\"" : "\"\\\"\"abc\"\"\"", StringUtils.enquoteIdentifier("\\\"abc\"", '\"', pdn, asq, bse), testCase);
            assertEquals("\"\\\\\"\"abc\"\"\"", StringUtils.enquoteIdentifier("\\\\\"abc\"", '\"', pdn, asq, bse), testCase);
            assertEquals(bse ? "\"\\\\\\\"abc\"\"\"" : "\"\\\\\\\"\"abc\"\"\"", StringUtils.enquoteIdentifier("\\\\\\\"abc\"", '\"', pdn, asq, bse), testCase);
            assertEquals(bse ? "\"\"\"abc\\\"\"" : pdn || !asq ? "\"\"\"abc\\\"\"\"" : "\"abc\\\"",
                    StringUtils.enquoteIdentifier("\"abc\\\"", '\"', pdn, asq, bse), testCase);
            assertEquals(pdn || !asq ? "\"\"\"abc\\\\\"\"\"" : "\"abc\\\\\"", StringUtils.enquoteIdentifier("\"abc\\\\\"", '\"', pdn, asq, bse), testCase);
            assertEquals(bse ? "\"\"\"abc\\\\\\\"\"" : pdn || !asq ? "\"\"\"abc\\\\\\\"\"\"" : "\"abc\\\\\\\"",
                    StringUtils.enquoteIdentifier("\"abc\\\\\\\"", '\"', pdn, asq, bse), testCase);
            assertEquals("\"abc\"\"def\"", StringUtils.enquoteIdentifier("abc\"def", '\"', pdn, asq, bse), testCase);
            assertEquals(bse ? "\"abc\\\"def\"" : "\"abc\\\"\"def\"", StringUtils.enquoteIdentifier("abc\\\"def", '\"', pdn, asq, bse), testCase);
            assertEquals("\"abc\\\\\"\"def\"", StringUtils.enquoteIdentifier("abc\\\\\"def", '\"', pdn, asq, bse), testCase);
            assertEquals("\"abc\"\"\"\"def\"", StringUtils.enquoteIdentifier("abc\"\"def", '\"', pdn, asq, bse), testCase);
            assertEquals(pdn || !asq ? "\"\"\"abc\"\"\"" : "\"abc\"", StringUtils.enquoteIdentifier("\"abc\"", '\"', pdn, asq, bse), testCase);
            assertEquals(pdn || !asq ? "\"\"\"abc\ndef\"\"\"" : "\"abc\ndef\"", StringUtils.enquoteIdentifier("\"abc\ndef\"", '\"', pdn, asq, bse), testCase);
            assertEquals("\"\"\"abc\"\"def\"\"\"", StringUtils.enquoteIdentifier("\"abc\"def\"", '\"', pdn, asq, bse), testCase);
            assertEquals(bse ? pdn || !asq ? "\"\"\"abc\\\"def\"\"\"" : "\"abc\\\"def\"" : "\"\"\"abc\\\"\"def\"\"\"",
                    StringUtils.enquoteIdentifier("\"abc\\\"def\"", '\"', pdn, asq, bse), testCase);
            assertEquals("\"\"\"abc\\\\\"\"def\"\"\"", StringUtils.enquoteIdentifier("\"abc\\\\\"def\"", '\"', pdn, asq, bse), testCase);
            assertEquals(pdn || !asq ? "\"\"\"abc\"\"\"\"def\"\"\"" : "\"abc\"\"def\"", StringUtils.enquoteIdentifier("\"abc\"\"def\"", '\"', pdn, asq, bse),
                    testCase);
            assertEquals(bse ? "\"abc\"\"def\\\"ghi\"" : "\"abc\"\"def\\\"\"ghi\"", StringUtils.enquoteIdentifier("abc\"def\\\"ghi", '\"', pdn, asq, bse),
                    testCase);
            assertEquals("\"abc\"\"def\\\\\"\"ghi\"", StringUtils.enquoteIdentifier("abc\"def\\\\\"ghi", '\"', pdn, asq, bse), testCase);
            assertEquals(bse ? "\"abc\"\"\"\"def\\\"ghi\"" : "\"abc\"\"\"\"def\\\"\"ghi\"",
                    StringUtils.enquoteIdentifier("abc\"\"def\\\"ghi", '\"', pdn, asq, bse), testCase);
            assertEquals("\"abc\"\"\"\"def\\\\\"\"ghi\"", StringUtils.enquoteIdentifier("abc\"\"def\\\\\"ghi", '\"', pdn, asq, bse), testCase);
            assertEquals("\"\"\"abc`\"", StringUtils.enquoteIdentifier("\"abc`", '\"', pdn, asq, bse), testCase);
            assertEquals(pdn || !asq ? "\"\"\"abc`def\"\"\"" : "\"abc`def\"", StringUtils.enquoteIdentifier("\"abc`def\"", '\"', pdn, asq, bse), testCase);
            assertEquals("\"\"\"abc`def\"\"\"\"ghi`\"", StringUtils.enquoteIdentifier("\"abc`def\"\"ghi`", '\"', pdn, asq, bse), testCase);
            assertEquals("\"\"\"abc`def\"\"ghi\"\"\"", StringUtils.enquoteIdentifier("\"abc`def\"ghi\"", '\"', pdn, asq, bse), testCase);
            assertEquals(pdn || !asq ? "\"\"\"abc`def\"\"\"\"ghi\"\"\"" : "\"abc`def\"\"ghi\"",
                    StringUtils.enquoteIdentifier("\"abc`def\"\"ghi\"", '\"', pdn, asq, bse), testCase);
        } while ((pdn = !pdn) || (asq = !asq) || (bse = !bse));
    }

    /**
     * Tests StringUtils.isSimpleIdentifier().
     *
     * @throws Exception
     */
    @Test
    void testIsSimpleIdentifier() throws Exception {
        IntFunction<String> repeatX = n -> IntStream.range(0, n).mapToObj(i -> "X").collect(Collectors.joining());
        Predicate<String> rwc = DatabaseMetaData::isReservedWord;

        boolean useRwc = false;
        do {
            // Valid examples.
            assertTrue(StringUtils.isSimpleIdentifier("a", useRwc ? rwc : null));
            assertTrue(StringUtils.isSimpleIdentifier("abc", useRwc ? rwc : null));
            assertTrue(StringUtils.isSimpleIdentifier("ab_cd", useRwc ? rwc : null));
            assertTrue(StringUtils.isSimpleIdentifier("ab$cd", useRwc ? rwc : null));
            assertTrue(StringUtils.isSimpleIdentifier("123abc", useRwc ? rwc : null));
            assertTrue(StringUtils.isSimpleIdentifier("_123", useRwc ? rwc : null));
            assertTrue(StringUtils.isSimpleIdentifier("$123", useRwc ? rwc : null));
            assertTrue(StringUtils.isSimpleIdentifier(repeatX.apply(64), useRwc ? rwc : null));

            // Invalid examples.
            assertFalse(StringUtils.isSimpleIdentifier(null, useRwc ? rwc : null));
            assertFalse(StringUtils.isSimpleIdentifier("", useRwc ? rwc : null));
            assertFalse(StringUtils.isSimpleIdentifier(" ", useRwc ? rwc : null));
            assertFalse(StringUtils.isSimpleIdentifier(".", useRwc ? rwc : null));
            assertFalse(StringUtils.isSimpleIdentifier("ab cd", useRwc ? rwc : null));
            assertFalse(StringUtils.isSimpleIdentifier("ab.cd", useRwc ? rwc : null));
            assertFalse(StringUtils.isSimpleIdentifier("ab-cd", useRwc ? rwc : null));
            assertFalse(StringUtils.isSimpleIdentifier("ab`cd", useRwc ? rwc : null));
            assertFalse(StringUtils.isSimpleIdentifier("ab\"cd", useRwc ? rwc : null));
            assertFalse(StringUtils.isSimpleIdentifier("`abc`", useRwc ? rwc : null));
            assertFalse(StringUtils.isSimpleIdentifier("123456", useRwc ? rwc : null));
            assertFalse(StringUtils.isSimpleIdentifier("\"abc\"", useRwc ? rwc : null));
            assertFalse(StringUtils.isSimpleIdentifier(repeatX.apply(65), useRwc ? rwc : null));
        } while (useRwc = !useRwc);

        assertTrue(DatabaseMetaData.isReservedWord("SELECT"));
        assertFalse(StringUtils.isSimpleIdentifier("SELECT", rwc));
        assertTrue(StringUtils.isSimpleIdentifier("SELECT", null));
        assertTrue(DatabaseMetaData.isReservedWord("update"));
        assertFalse(StringUtils.isSimpleIdentifier("update", rwc));
        assertTrue(StringUtils.isSimpleIdentifier("update", null));
    }

}
