int (@NotNull String text, @NotNull Callback callback) { State state = State.TEXT; int dataStartOffset = 0; int tagNameStartOffset = 0; String tagName = null; int i = 0; for (; i < text.length(); i++) { char c = text.charAt(i); switch (state) { case TEXT -> { if (c == '<') { if (i > dataStartOffset) { if (!callback.onText(text.substring(dataStartOffset, i).replace("&nbsp;", " "))) { return dataStartOffset; } } dataStartOffset = i; if (i < text.length() - 1 && text.charAt(i + 1) == '/') { state = State.INSIDE_CLOSE_TAG; tagNameStartOffset = ++i + 1; } else { state = State.INSIDE_OPEN_TAG; tagNameStartOffset = i + 1; } } } case INSIDE_OPEN_TAG -> { if (c == ' ') { tagName = text.substring(tagNameStartOffset, i); } else if (c == '/') { if (i < text.length() - 1 && text.charAt(i + 1) == '>') { if (tagName == null) { tagName = text.substring(tagNameStartOffset, i); } if (!callback.onStandaloneTag(tagName, text.substring(dataStartOffset, i + 2))) { return dataStartOffset; } tagName = null; state = State.TEXT; dataStartOffset = ++i + 1; } } else if (c == '>') { if (tagName == null) { tagName = text.substring(tagNameStartOffset, i); } if (!callback.onOpenTag(tagName, text.substring(dataStartOffset, i + 1))) { return dataStartOffset; } tagName = null; state = State.TEXT; dataStartOffset = i + 1; } } case INSIDE_CLOSE_TAG -> { if (c == '>') { tagName = text.substring(tagNameStartOffset, i); if (!callback.onCloseTag(tagName, text.substring(dataStartOffset, i + 1))) { return dataStartOffset; } tagName = null; state = State.TEXT; dataStartOffset = i + 1; } } } } if (dataStartOffset < text.length()) { callback.onText(text.substring(dataStartOffset).replace("&nbsp;", " ")); } return i; }