package info.bliki.wiki.filter;

import info.bliki.htmlcleaner.ContentToken;
import info.bliki.htmlcleaner.TagNode;
import info.bliki.htmlcleaner.TagToken;
import info.bliki.wiki.model.Configuration;
import info.bliki.wiki.model.IWikiModel;
import info.bliki.wiki.tags.HTMLTag;
import info.bliki.wiki.tags.WPBoldItalicTag;
import info.bliki.wiki.tags.WPTag;
import info.bliki.wiki.tags.util.TagStack;

import java.util.Map;

public abstract class AbstractParser extends WikipediaScanner {
  public static final String[] TOC_IDENTIFIERS = { "TOC", "NOTOC", "FORCETOC" };

  final static String HEADER_STRINGS[] = {
    "=",
    "==",
    "===",
    "====",
    "=====",
    "======" };

  final static int TokenNotFound = -2;

  final static int TokenIgnore = -1;

  final static int TokenSTART = 0;

  final static int TokenEOF = 1;

  final static int TokenBOLD = 3;

  final static int TokenITALIC = 4;

  final static int TokenBOLDITALIC = 5;

  final static int TokenUNDERLNE = 6;

  final static int TokenDELETEDLINE = 7;

  final static HTMLTag BOLD = new WPTag("b");

  final static HTMLTag ITALIC = new WPTag("i");

  final static HTMLTag BOLDITALIC = new WPBoldItalicTag();

  final static HTMLTag STRONG = new WPTag("strong");

  final static HTMLTag EM = new WPTag("em");

  final static HTMLTag UNDERLINE = new WPTag("u");

  final static HTMLTag DELETEDLINE = new WPTag("del");

  /**
   * The current scanned character
   */
  protected char fCurrentCharacter;

  /**
   * The current offset in the character source array
   */
  protected int fCurrentPosition;

  protected boolean fWhiteStart = false;

  protected int fWhiteStartPosition = 0;

  public AbstractParser(String stringSource) {
    super(stringSource);
    fCurrentPosition = 0;
    fCurrentCharacter = '\000';
    fWhiteStart = false;
    fWhiteStartPosition = 0;
  }

  /**
   * Read the characters until the given string is found and set the current
   * position of the parser behind the found string.
   * 
   * @param untilString
   * @return <code>true</code> if the string was found; <code>false</code>
   *         otherwise
   */
  protected final boolean readUntil(String untilString) {
    int index = fStringSource.indexOf(untilString, fCurrentPosition);
    if (index != (-1)) {
      fCurrentPosition = index + untilString.length();
      return true;
    }
    return false;
  }

  /**
   * Read the characters until the concatenated <i>start</i> and <i>end</i>
   * substring is found. The end substring is matched ignoring case
   * considerations.
   * 
   * @param startString
   *          the start string which should be searched in exact case mode
   * @param endString
   *          the end string which should be searched in ignore case mode
   * @return
   */
  protected final int readUntilIgnoreCase(String startString, String endString) {
    int index =
      Util.indexOfIgnoreCase(
        fStringSource,
        startString,
        endString,
        fCurrentPosition);
    if (index != (-1)) {
      fCurrentPosition = index + startString.length() + endString.length();
      return startString.length() + endString.length();
    }
    fCurrentPosition = fStringSource.length();
    return 0;
  }

  /**
   * Read the characters until the <code>&lt;</code> character with following
   * <i>end</i> string is found. The end string is matched ignoring case
   * considerations.
   * 
   * @param endString
   *          the end string which should be searched in ignore case mode
   * @return
   */

  protected final int readUntilNestedIgnoreCase(String endString) {
    int index =
      Util.indexOfNestedIgnoreCase(fStringSource, endString, fCurrentPosition);
    if (index != (-1)) {
      fCurrentPosition = index + 2 + endString.length();
      return 2 + endString.length();
    }
    fCurrentPosition = fStringSource.length();
    return 0;
  }

  /**
   * Read until character is found
   * 
   * @param testedChar
   *          search the next position of this char
   * @return <code>true</code> if the tested character can be found
   */
  protected final boolean readUntilChar(char testedChar) {
    int temp = fCurrentPosition;
    try {
      while ((fCurrentCharacter = fSource[fCurrentPosition++]) != testedChar) {
      }
      return true;
    } catch (IndexOutOfBoundsException e) {
      fCurrentPosition = temp;
      return false;
    }
  }

  /**
   * Read until character is found or stop at end-of-line
   * 
   * @param testedChar
   *          search the next position of this char
   * @return <code>true</code> if the tested character can be found
   */
  protected final boolean readUntilCharOrStopAtEOL(char testedChar) {
    int temp = fCurrentPosition;
    boolean attrMode = false; // allow newline characters in attributes
    try {
      while ((fCurrentCharacter = fSource[fCurrentPosition++]) != testedChar) {
        if (attrMode) {
          if (fCurrentCharacter == '"') {
            attrMode = false;
          }
        } else {
          if (fCurrentCharacter == '\n' || fCurrentCharacter == '\r') {
            return false;
          }
          if (fCurrentCharacter == '"') {
            attrMode = true;
          }
        }
      }
      return true;
    } catch (IndexOutOfBoundsException e) {
      fCurrentPosition = temp;
      return false;
    }
  }

  /**
   * Read until the end-of-line characters (i.e. '\r' or '\n') or the end of the
   * string is reached
   * 
   * @param fName
   * @return <code>true</code> if the end-of-line characters or the end of the
   *         string is reached
   * 
   */
  protected final boolean readUntilEOL() {
    try {
      while (true) {
        fCurrentCharacter = fSource[fCurrentPosition++];
        if (fCurrentCharacter == '\n' || fCurrentCharacter == '\r') {
          return true;
        }
        if (fCurrentCharacter == '<') {
          int newPos = readSpecialWikiTags(fCurrentPosition);
          if (newPos >= 0) {
            fCurrentPosition = newPos;
          }
        }
      }
    } catch (IndexOutOfBoundsException e) {
      --fCurrentPosition;
      return true;
    }
  }

  protected boolean isEmptyLine(int diff) {
    int temp = fCurrentPosition - diff;
    char ch;
    try {
      while (true) {
        ch = fSource[temp++];
        if (!Character.isWhitespace(ch)) {
          return false;
        }
        if (ch == '\n') {
          return true;
        }
      }
    } catch (IndexOutOfBoundsException e) {
      // ..
    }
    return true;
  }

  protected int readWhitespaceUntilEndOfLine(int diff) {
    int temp = fCurrentPosition - diff;
    char ch;
    while (fSource.length > temp) {
      ch = fSource[temp];
      if (!Character.isWhitespace(ch)) {
        return -1;
      }
      if (ch == '\n') {
        fCurrentPosition = temp;
        return temp;
      }
      temp++;
    }
    fCurrentPosition = temp - 1;
    return temp;
  }

  protected int readWhitespaceUntilStartOfLine(int diff) {
    int temp = fCurrentPosition - diff;
    char ch;

    while (temp >= 0) {
      ch = fSource[temp];
      if (!Character.isWhitespace(ch)) {
        return -1;
      }
      if (ch == '\n') {
        return temp;
      }
      temp--;
    }

    return temp;
  }

  protected boolean parsePHPBBCode(String name, StringBuilder bbCode) {
    int index = 1;
    char ch = ' ';

    while (index < name.length()) {
      ch = name.charAt(index++);
      if ('a' <= ch && ch <= 'z') {
        bbCode.append(ch);
      } else {
        break;
      }
    }
    if (ch != '=' && index != name.length()) {
      // no bbcode
      return false;
    }
    String bbStr = bbCode.toString();
    // String bbEndStr = "[/" + bbStr + "]";
    String bbEndStr = bbStr + "]";
    int startPos = fCurrentPosition;

    readUntilIgnoreCase("[/", bbEndStr);

    String bbAttr = null;
    if (ch == '=') {
      bbAttr = name.substring(index, name.length());
      if (bbAttr != null) {
        bbAttr = bbAttr.trim();
      }
    }

    int endPos = fCurrentPosition - bbEndStr.length() - 2;
    String innerTag = fStringSource.substring(startPos, endPos);

    return createBBCode(bbStr, bbAttr, innerTag);
  }

  private int parsePHPBBCodeRecursive(String rawWikitext, int index) {
    char ch = ' ';
    StringBuilder bbCode = new StringBuilder(10);

    while (index < rawWikitext.length()) {
      ch = rawWikitext.charAt(index++);
      if ('a' <= ch && ch <= 'z') {
        bbCode.append(ch);
      } else {
        break;
      }
    }

    String bbStr = bbCode.toString();
    String bbEndStr = bbStr + "]";
    int startPos = index;

    int endIndex = Util.indexOfIgnoreCase(rawWikitext, "[/", bbEndStr, index);
    if (endIndex != (-1)) {

      String bbAttr = null;
      if (ch == '=') {
        bbAttr = rawWikitext.substring(index, endIndex);
        if (bbAttr != null) {
          bbAttr = bbAttr.trim();
        }
      }

      String innerTag = rawWikitext.substring(startPos, endIndex);

      if (createBBCode(bbStr, bbAttr, innerTag)) {
        return endIndex + 3 + bbStr.length();
      }
    }
    return -1;

  }

  private boolean createBBCode(String bbStr, String bbAttr, String innerTag) {
    if (bbStr.equals("code")) {
      TagNode preTagNode = new TagNode("pre");
      preTagNode.addAttribute("class", "code", true);
      preTagNode.addChild(new ContentToken(innerTag));
      fWikiModel.append(preTagNode);
      return true;
    } else if (bbStr.equals("color")) {
      if (bbAttr == null) {
        return false;
      }
      TagNode fontTagNode = new TagNode("font");
      fontTagNode.addAttribute("color", bbAttr, true);
      fontTagNode.addChild(new ContentToken(innerTag));
      fWikiModel.append(fontTagNode);

      return true;
    } else if (bbStr.equals("email")) {
      TagNode aTagNode = new TagNode("a");
      aTagNode.addAttribute("href", "emailto:" + innerTag.trim(), true);
      aTagNode.addChild(new ContentToken(innerTag));
      fWikiModel.append(aTagNode);

      return true;
    } else if (bbStr.equals("list")) {
      int listStart = 0;
      int listEnd = 0;
      TagNode listTagNode;
      if (bbAttr != null) {
        if (bbAttr.equals("a")) {
          listTagNode = new TagNode("ul");
        } else {
          listTagNode = new TagNode("ol");
        }
      } else {
        listTagNode = new TagNode("ul");
      }
      fWikiModel.pushNode(listTagNode);
      try {
        while (listEnd >= 0) {
          listEnd = innerTag.indexOf("[*]", listStart);
          if (listEnd > listStart) {
            if (listStart == 0) {
              parseNextPHPBBCode(innerTag.substring(0, listEnd));
              // fWikiModel.append(new ContentToken(innerTag.substring(0,
              // listEnd)));
            } else {
              listTagNode = new TagNode("li");
              fWikiModel.pushNode(listTagNode);
              try {
                parseNextPHPBBCode(innerTag.substring(listStart, listEnd));
                // listTagNode.addChild(new
                // ContentToken(innerTag.substring(listStart, listEnd)));
              } finally {
                fWikiModel.popNode();
              }
            }
            listStart = listEnd + 3;
          }
        }
        if (listStart == 0) {
          parseNextPHPBBCode(innerTag);
          // fWikiModel.append(new ContentToken(innerTag));
        } else {
          if (listStart < innerTag.length()) {
            listTagNode = new TagNode("li");
            fWikiModel.pushNode(listTagNode);
            try {
              parseNextPHPBBCode(innerTag.substring(listStart, innerTag
                .length()));
              // listTagNode.addChild(new
              // ContentToken(innerTag.substring(listStart,
              // innerTag.length())));
              // fWikiModel.append(listTagNode);
            } finally {
              fWikiModel.popNode();
            }
          }
        }

      } finally {
        fWikiModel.popNode();
      }
      return true;
    } else if (bbStr.equals("img")) {
      TagNode imgTagNode = new TagNode("img");
      imgTagNode.addAttribute("src", innerTag.trim(), true);
      imgTagNode.addChild(new ContentToken(innerTag));
      fWikiModel.append(imgTagNode);

      return true;
    } else if (bbStr.equals("quote")) {
      TagNode quoteTagNode = new TagNode("blockquote");
      // quoteTagNode.addChild(new ContentToken(innerTag));
      fWikiModel.pushNode(quoteTagNode);
      try {
        parseNextPHPBBCode(innerTag);
      } finally {
        fWikiModel.popNode();
      }
      return true;
    } else if (bbStr.equals("size")) {
      if (bbAttr == null) {
        return false;
      }
      TagNode fontTagNode = new TagNode("font");
      fontTagNode.addAttribute("size", bbAttr, true);
      fontTagNode.addChild(new ContentToken(innerTag));
      fWikiModel.append(fontTagNode);

      return true;
    } else if (bbStr.equals("url")) {
      if (bbAttr != null) {
        TagNode aTagNode = new TagNode("a");
        aTagNode.addAttribute("href", bbAttr, true);
        aTagNode.addChild(new ContentToken(innerTag));
        fWikiModel.append(aTagNode);

        return true;
      } else {
        TagNode aTagNode = new TagNode("a");
        aTagNode.addAttribute("href", innerTag.trim(), true);
        aTagNode.addChild(new ContentToken(innerTag));
        fWikiModel.append(aTagNode);

        return true;
      }
    } else if (bbStr.equals("b")) {
      TagNode boldTagNode = new TagNode("b");
      boldTagNode.addChild(new ContentToken(innerTag));
      fWikiModel.append(boldTagNode);
      return true;
    } else if (bbStr.equals("i")) {
      TagNode italicTagNode = new TagNode("i");
      italicTagNode.addChild(new ContentToken(innerTag));
      fWikiModel.append(italicTagNode);
      return true;
    } else if (bbStr.equals("u")) {
      TagNode underlineTagNode = new TagNode("u");
      underlineTagNode.addChild(new ContentToken(innerTag));
      fWikiModel.append(underlineTagNode);
      return true;
    }

    return false;
  }

  protected final boolean getNextChar(char testedChar) {
    int temp = fCurrentPosition;
    try {
      fCurrentCharacter = fSource[fCurrentPosition++];
      if (fCurrentCharacter != testedChar) {
        fCurrentPosition = temp;
        return false;
      }
      return true;

    } catch (IndexOutOfBoundsException e) {
      fCurrentPosition = temp;
      return false;
    }
  }

  protected final int getNextChar(char testedChar1, char testedChar2) {
    int temp = fCurrentPosition;
    try {
      int result;
      fCurrentCharacter = fSource[fCurrentPosition++];
      if (fCurrentCharacter == testedChar1) {
        result = 0;
      } else if (fCurrentCharacter == testedChar2) {
        result = 1;
      } else {
        fCurrentPosition = temp;
        return -1;
      }
      return result;
    } catch (IndexOutOfBoundsException e) {
      fCurrentPosition = temp;
      return -1;
    }
  }

  protected final boolean getNextCharAsDigit() {
    int temp = fCurrentPosition;
    try {
      fCurrentCharacter = fSource[fCurrentPosition++];
      if (!Character.isDigit(fCurrentCharacter)) {
        fCurrentPosition = temp;
        return false;
      }
      return true;
    } catch (IndexOutOfBoundsException e) {
      fCurrentPosition = temp;
      return false;
    }
  }

  protected final boolean getNextCharAsWhitespace() {
    int temp = fCurrentPosition;
    try {
      fCurrentCharacter = fSource[fCurrentPosition++];
      if (!Character.isWhitespace(fCurrentCharacter)) {
        fCurrentPosition = temp;
        return false;
      }
      return true;
    } catch (IndexOutOfBoundsException e) {
      fCurrentPosition = temp;
      return false;
    }
  }

  protected final boolean getNextCharAsDigit(int radix) {

    int temp = fCurrentPosition;
    try {
      fCurrentCharacter = fSource[fCurrentPosition++];

      if (Character.digit(fCurrentCharacter, radix) == -1) {
        fCurrentPosition = temp;
        return false;
      }
      return true;
    } catch (IndexOutOfBoundsException e) {
      fCurrentPosition = temp;
      return false;
    }
  }

  protected final int getNumberOfChar(char testedChar) {
    int number = 0;
    try {
      while ((fCurrentCharacter = fSource[fCurrentPosition++]) == testedChar) {
        number++;
      }
    } catch (IndexOutOfBoundsException e) {

    }
    fCurrentPosition--;
    return number;
  }

  protected final boolean getNextCharAsWikiPluginIdentifierPart() {
    int temp = fCurrentPosition;
    try {
      fCurrentCharacter = fSource[fCurrentPosition++];

      if (!Encoder.isWikiPluginIdentifierPart(fCurrentCharacter)) {
        fCurrentPosition = temp;
        return false;
      }
      return true;
    } catch (IndexOutOfBoundsException e) {
      fCurrentPosition = temp;
      return false;
    }
  }

  private void parseNextPHPBBCode(String rawWikitext) {
    int index = rawWikitext.indexOf('[');
    int lastIndex = 0;
    int tempIndex = -1;
    if (index < 0) {
      fWikiModel.append(new ContentToken(rawWikitext));
      return;
    }
    try {
      while (index >= 0) {
        String temp = rawWikitext.substring(lastIndex, index);
        if (temp.length() > 0) {
          fWikiModel.append(new ContentToken(temp));
        }
        tempIndex = parsePHPBBCodeRecursive(rawWikitext, index + 1);
        if (tempIndex < 0) {
          lastIndex = index + 1;
          index = rawWikitext.indexOf('[', index + 1);
        } else {
          lastIndex = tempIndex;
          index = rawWikitext.indexOf('[', tempIndex);
        }
      }
    } catch (IndexOutOfBoundsException e) {
      //
    }
    if (lastIndex < rawWikitext.length()) {
      fWikiModel.append(new ContentToken(rawWikitext.substring(lastIndex)));
    }
  }

  public TagStack parseRecursiveInternal(IWikiModel wikiModel,
      boolean createOnlyLocalStack, boolean noTOC) {
    // local stack for this wiki snippet
    TagStack localStack = new TagStack();
    // global wiki model stack
    TagStack globalWikiModelStack = wikiModel.swapStack(localStack);
    try {
      // fix for infinite recursion

      int level = wikiModel.incrementRecursionLevel();
      // int parserRecursionLevel = wikiModel.incrementParserRecursionLevel();
      // if (parserRecursionLevel > Configuration.PARSER_RECURSION_LIMIT) {
      // TagNode error = new TagNode("span");
      // error.addAttribute("class", "error", true);
      // error.addChild(new
      // ContentToken("Error - total recursion count limit exceeded parsing wiki tags."));
      // localStack.append(error);
      // return localStack;
      // }

      if (level > Configuration.PARSER_RECURSION_LIMIT) {
        TagNode error = new TagNode("span");
        error.addAttribute("class", "error", true);
        error.addChild(new ContentToken(
          "Error - recursion limit exceeded parsing wiki tags."));
        localStack.append(error);
        return localStack;
      }
      // WikipediaParser parser = new WikipediaParser(rawWikitext,
      // wikiModel.isTemplateTopic(), wikiModel.getWikiListener());
      setModel(wikiModel);
      setNoToC(noTOC);
      runParser();
      return localStack;
    } catch (Exception e) {
      e.printStackTrace();
      TagNode error = new TagNode("span");
      error.addAttribute("class", "error", true);
      error.addChild(new ContentToken(e.getClass().getSimpleName()));
      localStack.append(error);
    } catch (Error e) {
      e.printStackTrace();
      TagNode error = new TagNode("span");
      error.addAttribute("class", "error", true);
      error.addChild(new ContentToken(e.getClass().getSimpleName()));
      localStack.append(error);
    } finally {
      wikiModel.decrementRecursionLevel();
      // wikiModel.decrementParserRecursionLevel();
      if (!createOnlyLocalStack) {
        // append the resursively parsed local stack to the global wiki
        // model
        // stack
        globalWikiModelStack.append(localStack);
      }
      wikiModel.swapStack(globalWikiModelStack);
    }

    return localStack;
  }

  /**
   * Read the characters until the end position of the current wiki link is
   * found.
   * 
   * @return <code>true</code> if the end of the wiki link was found.
   */
  protected final boolean findWikiLinkEnd() {
    char ch;
    int level = 1;
    int position = fCurrentPosition;
    boolean pipeSymbolFound = false;
    try {
      while (true) {
        ch = fSource[position++];
        if (ch == '|') {
          pipeSymbolFound = true;
        } else if (ch == '[' && fSource[position] == '[') {
          if (pipeSymbolFound) {
            level++;
            position++;
          } else {
            return false;
          }
        } else if (ch == ']' && fSource[position] == ']') {
          position++;
          if (--level == 0) {
            break;
          }
        } else if (ch == '{' || ch == '}' || ch == '<' || ch == '>') {
          if (!pipeSymbolFound) {
            // see
            // http://en.wikipedia.org/wiki/Help:Page_name#Special_characters
            return false;
          }
        }

        if ((!pipeSymbolFound) && (ch == '\n' || ch == '\r')) {
          return false;
        }
      }
      fCurrentPosition = position;
      return true;
    } catch (IndexOutOfBoundsException e) {
      return false;
    }
  }

  public abstract void setNoToC(boolean noToC);

  public abstract void runParser();

  public static String getRedirectedTemplateContent(IWikiModel wikiModel,
      String redirectedLink, Map<String, String> templateParameters) {
    String redirNamespace = "";
    String redirArticle = redirectedLink;
    int index = redirectedLink.indexOf(":");
    if (index > 0) {
      redirNamespace = redirectedLink.substring(0, index);
      if (wikiModel.isTemplateNamespace(redirNamespace)) {
        redirArticle = redirectedLink.substring(index + 1);
        try {
          int level = wikiModel.incrementRecursionLevel();
          if (level > Configuration.PARSER_RECURSION_LIMIT) {
            return "Error - getting content of redirected template link: "
              + redirNamespace
              + ":"
              + redirArticle;
          }
          return wikiModel.getRawWikiContent(
            redirNamespace,
            redirArticle,
            templateParameters);
        } finally {
          wikiModel.decrementRecursionLevel();
        }
      }
    }
    return null;
  }

  /**
   * Check the text for a <code>#REDIRECT [[...]]</code> or
   * <code>#redirect [[...]]</code> link
   * 
   * @param rawWikiText
   *          the wiki text
   * @param wikiModel
   * @return <code>null</code> if a redirect was found and further parsing
   *         should be canceled according to the model.
   */
  public static String parseRedirect(String rawWikiText, IWikiModel wikiModel) {
    int redirectStart = -1;
    int redirectEnd = -1;
    for (int i = 0; i < rawWikiText.length(); i++) {
      if (rawWikiText.charAt(i) == '#') {
        if (startsWith(rawWikiText, i + 1, "redirect", true)) {
          redirectStart = rawWikiText.indexOf("[[", i + 8);
          if (redirectStart > i + 8) {
            redirectStart += 2;
            redirectEnd = rawWikiText.indexOf("]]", redirectStart);
          }
        }
        break;
      }
      if (Character.isWhitespace(rawWikiText.charAt(i))) {
        continue;
      }
      break;
    }

    if (redirectEnd >= 0) {
      String redirectedLink = rawWikiText.substring(redirectStart, redirectEnd);
      if (wikiModel.appendRedirectLink(redirectedLink)) {
        return redirectedLink;
      }
    }
    return null;
  }

  /**
   * Copy the read ahead content in the resulting HTML text token.
   * 
   * @param diff
   *          subtract <code>diff</code> form the current parser position to get
   *          the HTML text token end position.
   */
  protected void createContentToken(final int diff) {
    if (fWhiteStart) {
      try {
        final int count = fCurrentPosition - diff - fWhiteStartPosition;
        if (count > 0) {
          fWikiModel.append(new ContentToken(fStringSource.substring(
            fWhiteStartPosition,
            fWhiteStartPosition + count)));
        }
      } finally {
        fWhiteStart = false;
      }
    }
  }

  /**
   * Reduce the current token stack completely
   */
  protected void reduceTokenStack() {
    while (fWikiModel.stackSize() > 0) {
      fWikiModel.popNode();
    }
  }

  protected void reduceTokenStackBoldItalic() {
    boolean found = false;
    while (fWikiModel.stackSize() > 0) {
      TagToken token = fWikiModel.peekNode();//
      if (token.equals(BOLD)
        || token.equals(ITALIC)
        || token.equals(BOLDITALIC)) {
        if (fWhiteStart) {
          found = true;
          createContentToken(1);
        }
        fWikiModel.popNode();
      } else {
        return;
      }
    }
    if (found) {
      fWhiteStart = true;
      fWhiteStartPosition = fCurrentPosition;
    }
  }

  /**
   * Reduce the current token stack until an allowed parent is at the top of the
   * stack
   */
  protected void reduceTokenStack(TagToken node) {
    String allowedParents = node.getParents();
    if (allowedParents != null) {
      TagToken tag;
      int index = -1;

      while (fWikiModel.stackSize() > 0) {
        tag = fWikiModel.peekNode();
        index = allowedParents.indexOf("|" + tag.getName() + "|");
        if (index < 0) {
          fWikiModel.popNode();
          if (tag.getName().equals(node.getName())) {
            // for wrong nested HTML tags like <table> <tr><td>number
            // 1<tr><td>number 2</table>
            break;
          }
        } else {
          break;
        }
      }
    } else {
      while (fWikiModel.stackSize() > 0) {
        fWikiModel.popNode();
      }
    }
  }

}
