Info (@NotNull String text) { Info result = new Info(); StringBuilder buffer = new StringBuilder(text); int offset = 0; while (offset < buffer.length()) { RichTextHandler handler = null; int richTextMarkStart = -1; for (RichTextHandler h : RICH_TEXT_HANDLERS) { int i = buffer.indexOf("<" + h.getMarker(), offset); if (i >= 0 && (handler == null || i < richTextMarkStart)) { richTextMarkStart = i; handler = h; } } if (handler == null) break; String marker = handler.getMarker(); int attrStart = richTextMarkStart + marker.length() + 1; int openingTagEnd = buffer.indexOf(">", richTextMarkStart); int openTagLength = openingTagEnd - richTextMarkStart + 1; Map<String, String> attributes = parseAttributes(buffer.substring(attrStart, openingTagEnd)); String closingTag = "</" + marker + ">"; int closingTagStart = buffer.indexOf(closingTag); assert closingTagStart > 0; handler.handle(result, attributes, richTextMarkStart, closingTagStart - openTagLength); buffer.delete(closingTagStart, closingTagStart + closingTag.length()); buffer.delete(richTextMarkStart, openingTagEnd + 1); offset = closingTagStart - openTagLength; } result.text = buffer.toString(); return result; }