static List<String> split(final String str, final char delimiterChar,
      final char escapeChar) throws IllegalArgumentException {
    if (str == null) {
      return null;
    }
    int len = str.length();
    if (len == 0) {
      return Collections.emptyList();
    }
    List<String> list = new ArrayList<String>();
    // Keeps track of offset of the passed string.
    int offset = 0;
    // Indicates start offset from which characters will be copied from original
    // string to destination string. Resets when an escape or delimiter char is
    // encountered.
    int startOffset = 0;
    StringBuilder builder = new StringBuilder(len);
    // Iterate over the string till we reach the end.
    while (offset < len) {
      if (str.charAt(offset) == escapeChar) {
        // An escape character must be followed by a delimiter or escape char
        // but we have reached the end and have no further character to look at.
        if (offset + 1 >= len) {
          throw new IllegalArgumentException(
              "Escape char not properly escaped.");
        }
        char nextChar = str.charAt(offset + 1);
        // Next character must be a delimiter or an escape char.
        if (nextChar != escapeChar && nextChar != delimiterChar) {
          throw new IllegalArgumentException(
              "Escape char or delimiter char not properly escaped.");
        }
        // Copy contents from the offset where last escape or delimiter char was
        // encountered.
        if (startOffset < offset) {
          builder.append(str.substring(startOffset, offset));
        }
        builder.append(nextChar);
        offset += 2;
        // Reset the start offset as an escape char has been encountered.
        startOffset = offset;
        continue;
      } else if (str.charAt(offset) == delimiterChar) {
        // A delimiter has been encountered without an escape character.
        // String needs to be split here. Copy remaining chars and add the
        // string to list.
        builder.append(str.substring(startOffset, offset));
        list.add(builder.toString().trim());
        // Reset the start offset as a delimiter has been encountered.
        startOffset = ++offset;
        builder = new StringBuilder(len - offset);
        continue;
      }
      offset++;
    }
    // Copy rest of the characters.
    if (!str.isEmpty()) {
      builder.append(str.substring(startOffset));
    }
    // Add the last part of delimited string to list.
    list.add(builder.toString().trim());
    return list;
  }
@Test
  void testSplitUsingEscapeAndDelimChar() throws Exception {
    List<String> list =
        TimelineReaderUtils.split("*!cluster!*!b**o***!xer!oozie**", '!', '*');
    String[] arr = new String[list.size()];
    arr = list.toArray(arr);
    assertArrayEquals(new String[]{"!cluster", "!b*o*!xer", "oozie*"}, arr);
    list = TimelineReaderUtils.split("*!cluster!*!b**o***!xer!!", '!', '*');
    arr = new String[list.size()];
    arr = list.toArray(arr);
    assertArrayEquals(new String[]{"!cluster", "!b*o*!xer", "", ""}, arr);
  }