protected void handleTagStart(
      List<BBCodeItem> bbCodeItems, Stack<String> tags, BBCodeToken bbCodeToken) {

    String startTag = bbCodeToken.getStartTag();

    if (_blockElements.contains(startTag)) {
      String currentTag = null;

      while (!tags.isEmpty()
          && ((currentTag = tags.lastElement()) != null)
          && _inlineElements.contains(currentTag)) {

        BBCodeToken currentTagBBCodeToken = new BBCodeToken(currentTag);

        handleTagEnd(bbCodeItems, tags, currentTagBBCodeToken);
      }
    }

    if (_selfCloseElements.contains(startTag) && startTag.equals(tags.lastElement())) {

      BBCodeToken tagBBCodeToken = new BBCodeToken(startTag);

      handleTagEnd(bbCodeItems, tags, tagBBCodeToken);
    }

    tags.push(startTag);

    BBCodeItem bbCodeItem = new BBCodeItem(TYPE_TAG_START, bbCodeToken.getAttribute(), startTag);

    bbCodeItems.add(bbCodeItem);
  }
  public List<BBCodeItem> parse(String text) {
    List<BBCodeItem> bbCodeItems = new ArrayList<>();

    BBCodeLexer bbCodeLexer = new BBCodeLexer(text);
    Stack<String> tags = new Stack<>();
    IntegerWrapper marker = new IntegerWrapper();

    BBCodeToken bbCodeToken = null;

    while ((bbCodeToken = bbCodeLexer.getNextBBCodeToken()) != null) {
      handleData(bbCodeItems, bbCodeLexer, marker, bbCodeToken, text);

      if (bbCodeToken.getStartTag() == null) {
        handleTagEnd(bbCodeItems, tags, bbCodeToken);

        continue;
      }

      handleTagStart(bbCodeItems, tags, bbCodeToken);

      String startTag = bbCodeToken.getStartTag();

      if (!startTag.equals("code")) {
        continue;
      }

      while (true) {
        bbCodeToken = bbCodeLexer.getNextBBCodeToken();

        if (bbCodeToken == null) {
          break;
        }

        String endTag = GetterUtil.getString(bbCodeToken.getEndTag());

        if (endTag.equals("code")) {
          break;
        }
      }

      handleData(bbCodeItems, bbCodeLexer, marker, bbCodeToken, text);

      if (bbCodeToken != null) {
        handleTagEnd(bbCodeItems, tags, bbCodeToken);
      } else {
        break;
      }
    }

    handleData(bbCodeItems, bbCodeLexer, marker, null, text);
    handleTagEnd(bbCodeItems, tags, null);

    return bbCodeItems;
  }
  protected void handleTagEnd(
      List<BBCodeItem> bbCodeItems, Stack<String> tags, BBCodeToken bbCodeToken) {

    int size = 0;

    if (bbCodeToken != null) {
      String endTag = bbCodeToken.getEndTag();

      for (size = tags.size() - 1; size >= 0; size--) {
        if (endTag.equals(tags.elementAt(size))) {
          break;
        }
      }
    }

    if (size >= 0) {
      for (int i = tags.size() - 1; i >= size; i--) {
        BBCodeItem bbCodeItem = new BBCodeItem(TYPE_TAG_END, null, tags.elementAt(i));

        bbCodeItems.add(bbCodeItem);
      }

      tags.setSize(size);
    }
  }
  protected void handleData(
      List<BBCodeItem> bbCodeItems,
      BBCodeLexer bbCodeLexer,
      IntegerWrapper marker,
      BBCodeToken bbCodeToken,
      String data) {

    int length = data.length();

    int lastIndex = length;

    if (bbCodeToken != null) {
      length = bbCodeToken.getStart();

      lastIndex = bbCodeLexer.getLastIndex();
    }

    if (length > marker.getValue()) {
      BBCodeItem bbCodeItem =
          new BBCodeItem(TYPE_DATA, null, data.substring(marker.getValue(), length));

      bbCodeItems.add(bbCodeItem);
    }

    marker.setValue(lastIndex);
  }