Package info.bliki.wiki.addon.trac

Source Code of info.bliki.wiki.addon.trac.TracParser

package info.bliki.wiki.addon.trac;

import info.bliki.htmlcleaner.ContentToken;
import info.bliki.htmlcleaner.EndTagToken;
import info.bliki.htmlcleaner.TagNode;
import info.bliki.htmlcleaner.TagToken;
import info.bliki.wiki.filter.AbstractParser;
import info.bliki.wiki.filter.Encoder;
import info.bliki.wiki.filter.StringPair;
import info.bliki.wiki.filter.Util;
import info.bliki.wiki.filter.WPList;
import info.bliki.wiki.filter.WPTable;
import info.bliki.wiki.model.Configuration;
import info.bliki.wiki.model.DefaultEventListener;
import info.bliki.wiki.model.IEventListener;
import info.bliki.wiki.model.IWikiModel;
import info.bliki.wiki.tags.DdTag;
import info.bliki.wiki.tags.DlTag;
import info.bliki.wiki.tags.DtTag;
import info.bliki.wiki.tags.HTMLBlockTag;
import info.bliki.wiki.tags.HTMLTag;
import info.bliki.wiki.tags.HrTag;
import info.bliki.wiki.tags.PTag;
import info.bliki.wiki.tags.TableOfContentTag;
import info.bliki.wiki.tags.WPBoldItalicTag;
import info.bliki.wiki.tags.WPPreTag;
import info.bliki.wiki.tags.WPTag;
import info.bliki.wiki.tags.util.Attribute;
import info.bliki.wiki.tags.util.IBodyTag;
import info.bliki.wiki.tags.util.INoBodyParsingTag;
import info.bliki.wiki.tags.util.NodeAttribute;
import info.bliki.wiki.tags.util.TagStack;
import info.bliki.wiki.tags.util.WikiTagNode;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import org.apache.commons.validator.EmailValidator;

/**
* A Trac syntax parser for the parsing of a Trac wiki source text.
*
*/
public class TracParser extends AbstractParser {
  // private 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 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");

  /**
   * "table of content"
   *
   */
  // private List<Object> fTableOfContent = null;

  private TableOfContentTag fTableOfContentTag = null;

  // private HashSet<String> fToCSet = null;

  private int fHeadCounter = 0;

  // private int fSectionCounter = 1;

  /**
   * Enable HTML tags
   */
  private boolean fHtmlCodes = true;

  private boolean fNoToC = false;

  private boolean fRenderTemplate = false;

  private boolean fForceToC = false;

  private IEventListener fEventListener = null;

  /**
   * Limits the recursive call of this parser to a depth of RECURSION_LIMIT
   */
  final static int RECURSION_LIMIT = 25;

  public TracParser(String stringSource, boolean renderTemplate) {
    this(stringSource, null);
  }

  public TracParser(String stringSource, IEventListener wikiListener) {
    super(stringSource);
    // fRenderTemplate = renderTemplate;
    if (wikiListener == null) {
      fEventListener = DefaultEventListener.CONST;
    } else {
      fEventListener = wikiListener;
    }
  }

  /**
   * copy the content in the resulting ContentToken
   *
   */
  private void createContentToken(boolean whiteStart, final int whiteStartPosition, final int diff) {
    if (whiteStart) {
      try {
        final int whiteEndPosition = fCurrentPosition - diff;
        int count = whiteEndPosition - whiteStartPosition;
        if (count > 0) {
          fWikiModel.append(new ContentToken(new String(fSource, whiteStartPosition, count)));
        }
      } finally {
        fWhiteStart = false;
      }
    }
  }

  protected final boolean isCharBefore(char testedChar) {
    try {
      if (fSource[fCurrentPosition - 2] != testedChar) {
        return false;
      }
      return true;
    } catch (IndexOutOfBoundsException e) {
      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 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 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;
    }
  }

  protected int getNextToken() // throws InvalidInputException
  {
    fWhiteStart = true;
    fWhiteStartPosition = fCurrentPosition;
    try {
      while (true) {
        fCurrentCharacter = fSource[fCurrentPosition++];

        // ---------Identify the next token-------------
        switch (fCurrentCharacter) {
        case '|':
          // Trac table handling
          if (parseTable()) {
            continue;
          }
          break;
        // case '{':
        // // dummy parsing of wikipedia templates for event listeners
        // if (parseTemplate()) {
        // } else {
        // // wikipedia table handling
        // if (parseTable()) {
        // continue;
        // }
        // }
        // break;
        // case '_': // TOC identifiers __NOTOC__, __FORCETOC__ ...
        // if (parseSpecialIdentifiers()) {
        // continue;
        // }
        // break;
        case '=': // wikipedia header ?
          if (parseSectionHeaders()) {
            continue;
          }
          break;
        case '!':
          if (fSource[fCurrentPosition] >= 'A' && fSource[fCurrentPosition] <= 'Z') {
            continue;
          }
          // ignore next character for wiki parsing
          fCurrentPosition++;
          continue;
        case '*': // <ul> list
        case '#': // <ol> list
          if (parseLists()) {
            continue;
          }
          break;
        case ':':
          if (parseSimpleDefinitionLists()) {
            continue;
          }
          break;
        case ';':
          if (parseDefinitionLists()) {
            continue;
          }
          break;
        case '-': // parse ---- as <hr>
          if (parseHorizontalRuler()) {
            continue;
          }
          break;
        // case ' ': // pre-formatted text?
        // case '\t':
        // if (parsePreformattedHorizontalRuler()) {
        // if (!fWhiteStart) {
        // fWhiteStart = true;
        // fWhiteStartPosition = fCurrentPosition;
        // } else {
        // createContentToken(fWhiteStart, fWhiteStartPosition, 1);
        // }
        // continue;
        // }
        // break;
        }
        if (isStartOfLine() && fWikiModel.getRecursionLevel() == 1) {
          if (fWikiModel.stackSize() > 0 && (fWikiModel.peekNode() instanceof PTag) && isEmptyLine(1)) {
            createContentToken(fWhiteStart, fWhiteStartPosition, 2);
            reduceTokenStack(Configuration.HTML_PARAGRAPH_OPEN);
          } else {
            if (!isEmptyLine(1)) {
              if (fWikiModel.stackSize() == 0) {
                addParagraph();
              } else {
                TagToken tag = fWikiModel.peekNode();
                if (tag instanceof WPPreTag) {
                  addParagraph();
                  // } else if (tag instanceof PTag) {
                  // createContentToken(fWhiteStart, fWhiteStartPosition, 2);
                  // reduceTokenStack(Configuration.HTML_PARAGRAPH_OPEN);
                } else {
                  String allowedParents = Configuration.HTML_PARAGRAPH_OPEN.getParents();
                  if (allowedParents != null) {

                    int index = -1;
                    index = allowedParents.indexOf("|" + tag.getName() + "|");
                    if (index >= 0) {
                      addParagraph();
                    }
                  }
                }
              }
            }
          }
        }

        // ---------Identify the next token-------------
        switch (fCurrentCharacter) {
        case '~':
          if (handleSourroundingTags2('~', "del", Configuration.HTML_DEL_OPEN)) {
            continue;
          }
          break;
        // if (fSource[fCurrentPosition] == '~') {
        // ++fCurrentPosition;
        // createContentToken(fWhiteStart, fWhiteStartPosition, 2);
        // if (fWikiModel.stackSize() > 0 &&
        // fWikiModel.peekNode().equals(Configuration.HTML_DEL_OPEN)) {
        // fWikiModel.popNode();
        // } else {
        // fWikiModel.pushNode(new WPTag("del"));
        // }
        // continue;
        // }
        // break;
        case '_':
          if (handleSourroundingTags2('_', "u", Configuration.HTML_U_OPEN)) {
            continue;
          }
          break;
        case ',':
          if (handleSourroundingTags2(',', "sub", Configuration.HTML_SUB_OPEN)) {
            continue;
          }
          break;
        case '^':
          if (handleSourroundingTags1("sup", Configuration.HTML_SUP_OPEN)) {
            continue;
          }
          break;
        case '{':
          if (fSource[fCurrentPosition++] == '{' && fSource[fCurrentPosition++] == '{') {
            int startPosition = fCurrentPosition;
            if (fSource[fCurrentPosition] == '\n') {
              int preStartPosition = startPosition + 1;
              if (readUntil("\n}}}")) {
                TagToken preTag = new HTMLTag("pre");
                createContentTag(startPosition, fCurrentPosition - 3, preTag);
                continue;
              }
            }
            if (readUntil("}}}")) {
              TagToken ttTag = new HTMLTag("tt");
              createContentTag(startPosition, fCurrentPosition - 3, ttTag);
              continue;
            }
          }
          break;
        case '`':
          int startPosition = fCurrentPosition;
          if (readUntil("`")) {
            createContentTag(startPosition, fCurrentPosition - 1, new HTMLTag("tt"));
            continue;
          }
          break;
        case '[':
          if (parseWikiLink()) {
            continue;
          }
          break;
        case '\'':
          if (getNextChar('\'')) {
            if (getNextChar('\'')) {
              if (getNextChar('\'')) {
                if (getNextChar('\'')) {
                  createContentToken(fWhiteStart, fWhiteStartPosition, 5);
                  return TokenBOLDITALIC;
                }
                fCurrentPosition -= 1;
                fWhiteStart = true;
                createContentToken(fWhiteStart, fWhiteStartPosition, 3);
                return TokenBOLD;
              }
              createContentToken(fWhiteStart, fWhiteStartPosition, 3);
              return TokenBOLD;
            }
            createContentToken(fWhiteStart, fWhiteStartPosition, 2);
            return TokenITALIC;
          }
          break;
        case 'A':
        case 'B':
        case 'C':
        case 'D':
        case 'E':
        case 'G':
        case 'J':
        case 'K':
        case 'L':
        case 'N':
        case 'O':
        case 'P':
        case 'Q':
        case 'R':
        case 'S':
        case 'T':
        case 'U':
        case 'V':
        case 'W':
        case 'X':
        case 'Y':
        case 'Z':
          if (parseCamelCaseNames(fCurrentCharacter)) {
            continue;
          }
          break;
        case 'f': // ftp://
        case 'F':
          if (parseFTPLinks()) {
            continue;
          }
          if (parseCamelCaseNames(fCurrentCharacter)) {
            continue;
          }
          break;
        case 'h': // http(s)://
        case 'H':
          if (parseHTTPLinks()) {
            continue;
          }
          if (parseCamelCaseNames(fCurrentCharacter)) {
            continue;
          }
          break;
        case 'i': // "ISBN ..."
        case 'I':
          if (parseISBNLinks()) {
            continue;
          }
          if (parseCamelCaseNames(fCurrentCharacter)) {
            continue;
          }
          break;
        case 'm': // mailto:
        case 'M':
          if (parseMailtoLinks()) {
            continue;
          }
          if (parseCamelCaseNames(fCurrentCharacter)) {
            continue;
          }
          break;
        case '<':
          if (fHtmlCodes) {
            int htmlStartPosition = fCurrentPosition;
            // HTML tags are allowed
            try {
              switch (fSource[fCurrentPosition]) {
              case '!': // <!-- HTML comment -->
                if (parseHTMLCommentTags()) {
                  continue;
                }
                break;
              default:

                if (fSource[fCurrentPosition] != '/') {
                  // opening HTML tag
                  WikiTagNode tagNode = parseTag(fCurrentPosition);
                  if (tagNode != null) {
                    String tagName = tagNode.getTagName();
                    TagToken tag = fWikiModel.getTokenMap().get(tagName);
                    if (tag != null) {
                      tag = (TagToken) tag.clone();

                      if (tag instanceof TagNode) {
                        TagNode node = (TagNode) tag;
                        List<NodeAttribute> attributes = tagNode.getAttributesEx();
                        Attribute attr;
                        for (int i = 1; i < attributes.size(); i++) {
                          attr = attributes.get(i);
                          node.addAttribute(attr.getName(), attr.getValue(), true);
                        }
                      }
                      if (tag instanceof HTMLTag) {
                        ((HTMLTag) tag).setTemplate(isTemplate());
                      }

                      createContentToken(fWhiteStart, fWhiteStartPosition, 1);

                      fCurrentPosition = fScannerPosition;

                      String allowedParents = tag.getParents();
                      if (allowedParents != null) {
                        reduceTokenStack(tag);
                      }
                      createTag(tag, tagNode, tagNode.getEndPosition());
                      return TokenIgnore;

                    }
                    break;
                  }
                } else {
                  // closing HTML tag
                  WikiTagNode tagNode = parseTag(++fCurrentPosition);
                  if (tagNode != null) {
                    String tagName = tagNode.getTagName();
                    TagToken tag = fWikiModel.getTokenMap().get(tagName);
                    if (tag != null) {
                      createContentToken(fWhiteStart, fWhiteStartPosition, 2);
                      fCurrentPosition = fScannerPosition;

                      if (fWikiModel.stackSize() > 0) {
                        TagToken topToken = fWikiModel.peekNode();
                        if (topToken.getName().equals(tag.getName())) {
                          fWikiModel.popNode();
                          return TokenIgnore;
                        } else {
                          if (tag.isReduceTokenStack()) {
                            reduceStackUntilToken(tag);
                          }
                        }
                      } else {
                      }
                      return TokenIgnore;
                    }
                    break;
                  }
                }
              }
            } catch (IndexOutOfBoundsException e) {
              // do nothing
            }
            fCurrentPosition = htmlStartPosition;
          }
          break;
        }
        if (!fWhiteStart) {
          fWhiteStart = true;
          fWhiteStartPosition = fCurrentPosition - 1;
        }

      }
      // -----------------end switch while try--------------------
    } catch (IndexOutOfBoundsException e) {
      // end of scanner text
    }
    try {
      createContentToken(fWhiteStart, fWhiteStartPosition, 1);
    } catch (IndexOutOfBoundsException e) {
      // end of scanner text
    }
    return TokenEOF;
  }

  private boolean handleSourroundingTags1(String tagName, TagToken tag) {
    createContentToken(fWhiteStart, fWhiteStartPosition, 2);
    if (fWikiModel.stackSize() > 0 && fWikiModel.peekNode().equals(tag)) {
      fWikiModel.popNode();
    } else {
      fWikiModel.pushNode(new HTMLTag(tagName));
    }
    return true;
  }

  private boolean handleSourroundingTags2(char testChar, String tagName, TagToken tag) {
    if (fSource[fCurrentPosition] == testChar) {
      ++fCurrentPosition;
      createContentToken(fWhiteStart, fWhiteStartPosition, 2);
      if (fWikiModel.stackSize() > 0 && fWikiModel.peekNode().equals(tag)) {
        fWikiModel.popNode();
      } else {
        fWikiModel.pushNode(new HTMLTag(tagName));
      }
      return true;
    }
    return false;
  }

  private void createContentTag(int startPosition, int endPosition, TagToken tag) {
    createContentToken(fWhiteStart, fWhiteStartPosition, 1);
    fWikiModel.pushNode(tag);
    // String subStr = fStringSource.substring(startPosition, endPosition);
    int count = endPosition - startPosition;
    if (count > 0) {
      fWikiModel.append(new ContentToken(new String(fSource, startPosition, count)));
    }
    fWikiModel.popNode();
  }

  private void addParagraph() {
    createContentToken(fWhiteStart, fWhiteStartPosition, 2);
    reduceTokenStack(Configuration.HTML_PARAGRAPH_OPEN);
    fWikiModel.pushNode(new PTag());
  }

  private boolean parseHTMLCommentTags() {
    int htmlStartPosition = fCurrentPosition;
    String htmlCommentString = fStringSource.substring(fCurrentPosition - 1, fCurrentPosition + 3);

    if (htmlCommentString.equals("<!--")) {
      fCurrentPosition += 3;
      if (readUntil("-->")) {
        String htmlCommentContent = new String(fSource, htmlStartPosition + 3, fCurrentPosition - htmlStartPosition - 6);
        if (htmlCommentContent != null) {
          createContentToken(fWhiteStart, fWhiteStartPosition, fCurrentPosition - htmlStartPosition + 1);
          return true;
        }
      }
    }
    return false;
  }

  private boolean parseISBNLinks() {
    int urlStartPosition = fCurrentPosition;
    boolean foundISBN = false;
    try {
      String urlString = fStringSource.substring(fCurrentPosition - 1, fCurrentPosition + 4);
      if (urlString.equalsIgnoreCase("isbn ")) {
        fCurrentPosition += 4;
        fCurrentCharacter = fSource[fCurrentPosition++];

        createContentToken(fWhiteStart, fWhiteStartPosition, 6);
        fWhiteStart = false;
        foundISBN = true;
        char ch;
        ch = fSource[fCurrentPosition++];
        while ((ch >= '0' && ch <= '9') || ch == '-') {
          ch = fSource[fCurrentPosition++];
        }
      }
    } catch (IndexOutOfBoundsException e) {
      if (!foundISBN) {
        // rollback work :-)
        fCurrentPosition = urlStartPosition;
      }
    }
    if (foundISBN) {
      String urlString = new String(fSource, urlStartPosition - 1, fCurrentPosition - urlStartPosition);
      fCurrentPosition--;
      fWikiModel.appendISBNLink(urlString);
      return true;
    }
    return false;
  }

  private boolean parseFTPLinks() {
    int urlStartPosition = fCurrentPosition;
    boolean foundUrl = false;
    try {
      String urlString = fStringSource.substring(fCurrentPosition - 1, fCurrentPosition + 2);
      if (urlString.equalsIgnoreCase("ftp")) {
        fCurrentPosition += 2;
        fCurrentCharacter = fSource[fCurrentPosition++];

        if (fCurrentCharacter == ':' && fSource[fCurrentPosition++] == '/' && fSource[fCurrentPosition++] == '/') {
          createContentToken(fWhiteStart, fWhiteStartPosition, 6);
          fWhiteStart = false;
          foundUrl = true;
          while (Encoder.isUrlIdentifierPart(fSource[fCurrentPosition++])) {
          }
        }
      }
    } catch (IndexOutOfBoundsException e) {
      if (!foundUrl) {
        // rollback work :-)
        fCurrentPosition = urlStartPosition;
      }
    }
    if (foundUrl) {
      String urlString = new String(fSource, urlStartPosition - 1, fCurrentPosition - urlStartPosition);
      fCurrentPosition--;
      fWikiModel.appendExternalLink(urlString, urlString, true);
      return true;
    }
    return false;
  }

  private boolean parseHTTPLinks() {
    int urlStartPosition = fCurrentPosition;
    boolean foundUrl = false;
    try {
      int diff = 7;
      String urlString = fStringSource.substring(fCurrentPosition - 1, fCurrentPosition + 3);
      if (urlString.equalsIgnoreCase("http")) {
        fCurrentPosition += 3;
        fCurrentCharacter = fSource[fCurrentPosition++];
        if (fCurrentCharacter == 's') { // optional
          fCurrentCharacter = fSource[fCurrentPosition++];
          diff++;
        }

        if (fCurrentCharacter == ':' && fSource[fCurrentPosition++] == '/' && fSource[fCurrentPosition++] == '/') {
          createContentToken(fWhiteStart, fWhiteStartPosition, diff);
          fWhiteStart = false;
          foundUrl = true;
          while (Encoder.isUrlIdentifierPart(fSource[fCurrentPosition++])) {
          }
        }
      }
    } catch (IndexOutOfBoundsException e) {
      if (!foundUrl) {
        // rollback work :-)
        fCurrentPosition = urlStartPosition;
      }
    }
    if (foundUrl) {
      String urlString = new String(fSource, urlStartPosition - 1, fCurrentPosition - urlStartPosition);
      fCurrentPosition--;
      fWikiModel.appendExternalLink(urlString, urlString, true);
      return true;
    }
    return false;
  }

  private boolean parseMailtoLinks() {
    int urlStartPosition = fCurrentPosition;
    int tempPosition = fCurrentPosition;
    boolean foundUrl = false;
    try {
      String urlString = fStringSource.substring(fCurrentPosition - 1, fCurrentPosition + 6);
      if (urlString.equalsIgnoreCase("mailto:")) {
        tempPosition += 6;
        fCurrentCharacter = fSource[tempPosition++];

        foundUrl = true;
        while (!Character.isWhitespace(fSource[tempPosition++])) {
        }
      }
    } catch (IndexOutOfBoundsException e) {
    }
    if (foundUrl) {
      String urlString = new String(fSource, urlStartPosition - 1, tempPosition - urlStartPosition);
      String email = urlString.substring(7);
      if (EmailValidator.getInstance().isValid(email)) {
        createContentToken(fWhiteStart, fWhiteStartPosition, 1);
        fWhiteStart = false;
        fCurrentPosition = tempPosition;
        fCurrentPosition--;
        fWikiModel.appendMailtoLink(urlString, urlString, true);
        return true;
      }

    }
    // rollback work :-)
    fCurrentPosition = urlStartPosition;
    return false;
  }

  private boolean parseCamelCaseNames(char firstChar) {
    if (firstChar >= 'A' && firstChar <= 'Z') {
      if (isCharBefore('!')) {
        return false;
      }
      int startPosition = fCurrentPosition;
      int lowerCaseCounter = 0;
      try {
        boolean camelCase = true;
        StringBuilder camelCasePageName = new StringBuilder(64);
        camelCasePageName.append(firstChar);
        char ch;
        while (true) {
          ch = fSource[fCurrentPosition++];
          if (ch >= 'a' && ch <= 'z') {
            camelCasePageName.append(ch);
            if (camelCase) {
              lowerCaseCounter++;
            }
            camelCase = false;
            continue;
          }
          if (ch >= 'A' && ch <= 'Z') {
            camelCasePageName.append(ch);
            camelCase = true;
            continue;
          }
          if (ch == '/') {
            camelCasePageName.append(ch);
            camelCase = false;
            continue;
          }
          break;
        }
        if (lowerCaseCounter >= 2) {
          fCurrentPosition--;
          createContentToken(fWhiteStart, fWhiteStartPosition, fCurrentPosition - startPosition + 1);
          fWhiteStart = false;
          String camelCaseStr = camelCasePageName.toString();
          fWikiModel.appendInternalLink(camelCaseStr, null, camelCaseStr, null, true);

          return true;
        }

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

  /**
   * Parse a wiki section starting with a '[' character
   *
   * @return <code>true</code> if a correct link was found
   */
  private boolean parseWikiLink() {
    int startLinkPosition = fCurrentPosition;
    if (getNextChar('[')) {
      return parseWikiTag();
    } else {
      createContentToken(fWhiteStart, fWhiteStartPosition, 1);
      fWhiteStart = false;

      if (readUntilChar(']')) {
        String name = new String(fSource, startLinkPosition, fCurrentPosition - startLinkPosition - 1);

        // bbcode start
        if (fWikiModel.parseBBCodes()) {
          // parse tokens like phpBB forum syntax style (bbcode)
          if (name.length() > 0) {
            StringBuilder bbCode = new StringBuilder(name.length());
            char ch = name.charAt(0);
            if ('a' <= ch && ch <= 'z') {
              // first character must be a letter
              bbCode.append(ch);
              if (parsePHPBBCode(name, bbCode)) {
                return true;
              }
            }
          }
        }
        // bbcode end

        if (handleHTTPLink(name)) {
          return true;
        }
        fCurrentPosition = startLinkPosition;
      }
    }
    return false;
  }

  /**
   * Parse a wiki section starting with a '[[' sequence
   *
   * @return <code>true</code> if a correct link was found
   */
  private boolean parseWikiTag() {
    int startLinkPosition = fCurrentPosition;
    int endLinkPosition;
    // wikipedia link style
    createContentToken(fWhiteStart, fWhiteStartPosition, 2);

    int temp = fCurrentPosition;
    if (findWikiLinkEnd()) {
      endLinkPosition = fCurrentPosition - 2;
      String name = new String(fSource, startLinkPosition, endLinkPosition - startLinkPosition);
      // test for a suffix string behind the Wiki link. Useful for
      // plurals.
      // Example:
      // Dolphins are [[aquatic mammal]]s that are closely related to
      // [[whale]]s
      // and [[porpoise]]s.
      temp = fCurrentPosition;
      StringBuilder suffixBuffer = new StringBuilder();

      try {
        while (true) {
          fCurrentCharacter = fSource[fCurrentPosition++];
          if (!Character.isLetterOrDigit(fCurrentCharacter)) {
            fCurrentPosition--;
            break;
          }
          suffixBuffer.append(fCurrentCharacter);
        }
        String suffix = suffixBuffer.toString();
        fEventListener.onWikiLink(fSource, startLinkPosition, endLinkPosition, suffix);
        fWikiModel.appendRawWikipediaLink(name, suffix);
        return true;
      } catch (IndexOutOfBoundsException e) {
        fCurrentPosition = temp;
      }

      fEventListener.onWikiLink(fSource, startLinkPosition, endLinkPosition, "");
      fWikiModel.appendRawWikipediaLink(name, "");
      return true;
    } else {
      fWhiteStart = true;
      fWhiteStartPosition = startLinkPosition - 2;
      fCurrentPosition = temp + 1;
    }
    return false;
  }

  private boolean parsePreformattedHorizontalRuler() {
    if (isStartOfLine() && !isEmptyLine(1)) {
      // if (fWikiModel.stackSize() == 0 ||
      // !fWikiModel.peekNode().equals("pre")) {
      if (fWikiModel.stackSize() == 0 || !(fWikiModel.peekNode() instanceof HTMLBlockTag)) {
        createContentToken(fWhiteStart, fWhiteStartPosition, 2);
        reduceTokenStack(Configuration.HTML_PRE_OPEN);

        // don't use Configuration.HTML_PRE_OPEN here
        // rendering differs between these tags!
        fWikiModel.pushNode(new WPPreTag());// (TagToken)

      }
      return true;
    }
    return false;
  }

  /**
   * Parse <code>----</code> as &lt;hr&gt; tag
   *
   * @return
   */
  private boolean parseHorizontalRuler() {
    if (isStartOfLine()) {
      int tempCurrPosition = fCurrentPosition;
      try {
        if (fSource[tempCurrPosition++] == '-' && fSource[tempCurrPosition++] == '-' && fSource[tempCurrPosition++] == '-') {
          int pos = isEndOfLine('-', tempCurrPosition);
          if (pos > 0) {
            HrTag hr = new HrTag();
            createContentToken(fWhiteStart, fWhiteStartPosition, 2);
            reduceTokenStack(hr);
            fCurrentPosition = pos;
            fWikiModel.append(hr);
            fWhiteStart = false;
            return true;
          }
        }
      } catch (IndexOutOfBoundsException e) {

      }
      fCurrentPosition = tempCurrPosition;
    }
    return false;
  }

  private boolean parseDefinitionLists() {
    if (isStartOfLine()) {
      createContentToken(fWhiteStart, fWhiteStartPosition, 1);

      int startHeadPosition = fCurrentPosition;
      if (readUntilEOL()) {
        TagToken dl = new DlTag();
        TagToken dt = new DtTag();
        reduceTokenStack(dl);
        String head = new String(fSource, startHeadPosition, fCurrentPosition - startHeadPosition);
        int index = head.indexOf(" : ");
        if (index > 0) {
          fWikiModel.pushNode(dl);
          fWikiModel.pushNode(dt);
          // fResultBuffer.append("<dl><dt>");
          TracParser.parseRecursive(head.substring(0, index).trim(), fWikiModel, false, true);
          // fResultBuffer.append("&nbsp;</dt><dd>");
          fWikiModel.popNode();
          fWikiModel.pushNode(new DdTag());
          TracParser.parseRecursive(head.substring(index + 2).trim(), fWikiModel, false, true);
          // fResultBuffer.append("\n</dd></dl>");
          fWikiModel.popNode();
          fWikiModel.popNode();
        } else {
          index = head.indexOf(":");
          if (index > 0) {
            fWikiModel.pushNode(dl);
            fWikiModel.pushNode(dt);
            // fResultBuffer.append("<dl><dt>");
            TracParser.parseRecursive(head.substring(0, index).trim(), fWikiModel, false, true);
            fWikiModel.popNode();
            fWikiModel.pushNode(new DdTag());
            // fResultBuffer.append("</dt><dd>");
            TracParser.parseRecursive(head.substring(index + 1).trim(), fWikiModel, false, true);
            fWikiModel.popNode();
            fWikiModel.popNode();
            // fResultBuffer.append("\n</dd></dl>");
          } else {
            fWikiModel.pushNode(dl);
            fWikiModel.pushNode(dt);
            // fResultBuffer.append("<dl><dt>");
            TracParser.parseRecursive(head.trim(), fWikiModel, false, true);
            fWikiModel.popNode();
            fWikiModel.popNode();
            // fResultBuffer.append("&nbsp;</dt></dl>");
          }
        }
        return true;
      }
    }
    return false;
  }

  private boolean parseSimpleDefinitionLists() {
    if (isStartOfLine()) {
      createContentToken(fWhiteStart, fWhiteStartPosition, 1);

      int levelHeader = getNumberOfChar(':') + 1;
      int startHeadPosition = fCurrentPosition;
      if (readUntilEOL()) {
        reduceTokenStack(Configuration.HTML_DL_OPEN);
        String head = new String(fSource, startHeadPosition, fCurrentPosition - startHeadPosition);
        for (int i = 0; i < levelHeader; i++) {
          fWikiModel.pushNode(new DlTag());
          fWikiModel.pushNode(new DdTag());
        }

        TracParser.parseRecursive(head.trim(), fWikiModel, false, true);

        for (int i = 0; i < levelHeader; i++) {
          // fResultBuffer.append("\n</dd></dl>");
          fWikiModel.popNode();
          fWikiModel.popNode();
        }
        return true;
      }
    }
    return false;
  }

  private boolean parseLists() {
    // set scanner pointer to '\n' character:
    if (isStartOfLine()) {
      setPosition(fCurrentPosition - 2);
      WPList list = wpList();
      if (list != null && !list.isEmpty()) {
        createContentToken(fWhiteStart, fWhiteStartPosition, 1);
        reduceTokenStack(list);
        fCurrentPosition = getPosition() - 1;
        fWikiModel.append(list);
        return true;
      }
    }
    return false;
    // // set scanner pointer to '\n' character:
    // if (isStartOfList()) {
    // setPosition(fCurrentPosition - 3);
    // WPList list = tracList();
    // if (list != null && !list.isEmpty()) {
    // createContentToken(fWhiteStart, fWhiteStartPosition, 1);
    // reduceTokenStack(list);
    // fCurrentPosition = getPosition() - 1;
    // fWikiModel.append(list);
    // return true;
    // }
    // }
    // return false;
  }

  private boolean parseSectionHeaders() {
    if (isStartOfLine()) {
      int headerStartPosition = fCurrentPosition - 1;
      int endIndex = fStringSource.indexOf("\n", fCurrentPosition);
      if (endIndex < 0) {
        endIndex = fStringSource.length();
      }
      int headerEndPosition = endIndex;
      char ch;
      while (headerEndPosition > 0) {
        ch = fSource[--headerEndPosition];
        if (!Character.isWhitespace(ch)) {
          break;
        }
      }
      if (headerEndPosition < 0 || headerEndPosition <= headerStartPosition) {
        return false;
      }
      int level = 0;
      while (headerStartPosition < headerEndPosition) {
        if (fSource[headerStartPosition] == '=' && fSource[headerEndPosition] == '=') {
          level++;
          headerStartPosition++;
          headerEndPosition--;
          if (level == 6) {
            headerEndPosition++;
            break;
          }
        } else {
          headerEndPosition++;
          break;
        }
      }
      if (level == 0) {
        return false;
      }
      createContentToken(fWhiteStart, fWhiteStartPosition, 1);
      reduceTokenStack();
      String head = "";
      if (headerEndPosition > headerStartPosition) {
        head = new String(fSource, headerStartPosition, headerEndPosition - headerStartPosition);
      }
      fEventListener.onHeader(fSource, headerStartPosition, headerEndPosition, level);
      fCurrentPosition = endIndex;

      handleHead(head, level);

      return true;
    }
    return false;
  }

  private boolean parseTable() {
    if (isStartOfLine()) {
      // Trac wiki table ?
      setPosition(fCurrentPosition - 1);
      WPTable table = tracTable(fTableOfContentTag);
      if (table != null) {
        createContentToken(fWhiteStart, fWhiteStartPosition, 1);
        reduceTokenStack(table);
        // set pointer behind: "\n|}"
        fCurrentPosition = getPosition();
        fWikiModel.append(table);
        // table.filter(fSource, fWikiModel);
        return true;
      }
    }
    return false;
  }

  private boolean parseTemplate() {
    // dummy parsing of Wikipedia templates for event listeners
    // doesn't change fCurrentPosition
    if (fSource[fCurrentPosition] == '{') {
      int templateStartPosition = fCurrentPosition + 1;
      if (fSource[templateStartPosition] != '{') {
        int templateEndPosition = findNestedTemplateEnd(fSource, templateStartPosition);
        if (templateEndPosition > 0) {
          fEventListener.onTemplate(fSource, templateStartPosition, templateEndPosition - 2);
          return true;
        }
      }
    }
    return false;
  }

  /**
   * Parse special identifiers like __TOC__, __NOTOC__, __FORCETOC__
   *
   * @return
   */
  // private boolean parseSpecialIdentifiers() {
  // if (fSource[fCurrentPosition] == '_') {
  // fCurrentPosition++;
  // int tocEndPosition = fCurrentPosition;
  // char ch;
  // while (true) {
  // ch = fSource[tocEndPosition++];
  // if (ch >= 'A' && ch <= 'Z') {
  // continue;
  // }
  // break;
  // }
  // if (ch == '_' && fSource[tocEndPosition] == '_') {
  // String tocIdent = fStringSource.substring(fCurrentPosition, tocEndPosition
  // - 1);
  // boolean tocRecognized = false;
  // for (int i = 0; i < TOC_IDENTIFIERS.length; i++) {
  // if (TOC_IDENTIFIERS[i].equals(tocIdent)) {
  // createContentToken(fWhiteStart, fWhiteStartPosition, 2);
  // tocRecognized = true;
  // fCurrentPosition = tocEndPosition + 1;
  // switch (i) {
  // case 0: // TOC
  // createTableOfContent(true);
  // fForceToC = true;
  // break;
  // case 1: // NOTOC
  // setNoToC(true);
  // break;
  // case 2: // FORCETOC
  // fForceToC = true;
  // break;
  // }
  // break;
  // }
  // }
  // if (tocRecognized) {
  // return true;
  // }
  // }
  // }
  // return false;
  // }
  /**
   * Check if the scanners cursor position is at the start of a line
   *
   * @return
   */
  private boolean isStartOfLine() {
    if (fCurrentPosition >= 2) {
      if (fSource[fCurrentPosition - 2] == '\n') {
        return true;
      }
    } else {
      if (fCurrentPosition == 1) {
        return true;
      }
    }
    return false;
  }

  private boolean isStartOfList() {
    if (fCurrentPosition >= 3) {
      if (fSource[fCurrentPosition - 3] == '\n' && fSource[fCurrentPosition - 2] == ' ') {
        return true;
      }
    } else if (fCurrentPosition == 2) {
      if (fSource[0] == ' ') {
        return true;
      }
    }
    return false;
  }

  private int isEndOfLine(char testChar, int currentPosition) {
    int tempPosition = currentPosition;
    try {
      char ch;
      while (true) {
        ch = fSource[tempPosition];
        if (ch != testChar) {
          break;
        }
        tempPosition++;
      }
      while (true) {
        ch = fSource[tempPosition++];
        if (ch == '\n') {
          return tempPosition;
        } else if (!Character.isWhitespace(ch)) {
          return -1;
        }
      }
    } catch (IndexOutOfBoundsException e) {

    }
    return -1;
  }

  private void createTag(TagToken tag, WikiTagNode tagNode, int startMacroPosition) {
    String endTag;
    String macroBodyString = "";
    int index0;
    String command = tagNode.getTagName();
    if ((tag != null) && (tag instanceof IBodyTag) && (!tagNode.isEmptyXmlTag())) {
      endTag = command + '>';
      index0 = Util.indexOfIgnoreCase(fStringSource, "</", endTag, startMacroPosition);

      if (index0 >= 0) {
        macroBodyString = fStringSource.substring(startMacroPosition, index0);
        fCurrentPosition = index0 + endTag.length() + 2;
      } else {
        macroBodyString = new String(fSource, startMacroPosition, fSource.length - startMacroPosition);
        fCurrentPosition = fSource.length;
      }
    } else {
      macroBodyString = null;
      fCurrentPosition = startMacroPosition;
    }

    handleTag(tag, tagNode, macroBodyString);
  }

  private boolean handleHTTPLink(String name) {
    String urlString;
    if (name != null) {
      boolean isEmail = false;
      urlString = name.trim();
      String email;
      String lowerCaseName = urlString.toLowerCase();
      if (lowerCaseName.startsWith("wiki:") && lowerCaseName.length() > 5) {
        int index = 5;
        int urlStart = 5;
        int urlEnd = urlString.length();
        if (lowerCaseName.charAt(5) == '"') {
          index = lowerCaseName.indexOf('"', index + 1);
          if (index < 0) {
            index = 6;
          } else {
            urlStart = 6;
            urlEnd = index;
          }
        }
        // Internal wiki link; name separated by space?
        int spaceIndex = urlString.indexOf(' ', index);
        String alias = "";
        if (spaceIndex != (-1)) {
          alias = urlString.substring(spaceIndex + 1);
          if (urlEnd < spaceIndex) {
            urlString = urlString.substring(urlStart, urlEnd);
          } else {
            urlString = urlString.substring(urlStart, spaceIndex);
          }
        } else {
          urlString = urlString.substring(urlStart, urlEnd);
          alias = urlString;
        }
        fWikiModel.appendInternalLink(urlString, null, alias, null, true);
        return true;
      }
      if (lowerCaseName.startsWith("\"") && lowerCaseName.length() > 1) {
        int index = 1;
        int urlStart = 1;
        int urlEnd = urlString.length();
        index = lowerCaseName.indexOf('"', index + 1);
        if (index < 0) {
          return false;
        } else {
          urlStart = 1;
          urlEnd = index;
        }
        urlString = urlString.substring(urlStart, urlEnd);
        fWikiModel.appendInternalLink(urlString, null, urlString, null, true);
        return true;
      }
      boolean prefixCheck = lowerCaseName.startsWith("http://");
      if (!prefixCheck) {
        prefixCheck = lowerCaseName.startsWith("https://");
      }
      if (!prefixCheck) {
        prefixCheck = lowerCaseName.startsWith("ftp://");
      }
      if (!prefixCheck) {
        prefixCheck = lowerCaseName.startsWith("mailto:");
        isEmail = true;
      }
      if (prefixCheck) {
        // name separated by space?
        int spaceIndex = urlString.indexOf(' ');
        String alias = "";
        if (spaceIndex != (-1)) {
          alias = urlString.substring(spaceIndex + 1);
          urlString = urlString.substring(0, spaceIndex);
        } else {
          alias = urlString;
        }

        if (isEmail) {
          if (spaceIndex > 7) {
            email = urlString.substring(7, spaceIndex);
          } else {
            email = urlString.substring(7);
          }
          if (EmailValidator.getInstance().isValid(email)) {
            fWikiModel.appendMailtoLink(urlString, alias, false);
            return true;
          }
        } else {
          fWikiModel.appendExternalLink(urlString, alias, false);
          return true;
        }

      }
    }
    return false;
  }

  /**
   *
   * @param urlString
   *          the url link
   * @param alias
   *          an alias for the url
   */
  // protected void createExternalLink(String urlString, String alias) {
  // fWikiModel.appendExternalLink(urlString, alias, false);
  // }
  // private void handleWikipediaLink(String linkText, String suffix) {
  // String name = linkText;
  // if (name != null) {
  // // trim the name for whitespace characters on the left side
  // int trimLeftIndex = 0;
  // while ((trimLeftIndex < name.length()) && (name.charAt(trimLeftIndex) <=
  // ' ')) {
  // trimLeftIndex++;
  // }
  // if (trimLeftIndex > 0) {
  // name = name.substring(trimLeftIndex);
  // }
  // // Is there an alias like [alias|link] ?
  // int pipeIndex = name.lastIndexOf('|');
  // String alias = "";
  // if (-1 != pipeIndex) {
  // alias = name.substring(pipeIndex + 1);
  // name = name.substring(0, pipeIndex);
  // if (alias.length() == 0) {
  // // special cases like: [[Test:hello world|]] or [[Test(hello
  // // world)|]]
  // // or [[Test, hello world|]]
  // alias = name;
  // int index = alias.indexOf(':');
  // if (index != -1) {
  // alias = alias.substring(index + 1).trim();
  // } else {
  // index = alias.indexOf('(');
  // if (index != -1) {
  // alias = alias.substring(0, index).trim();
  // } else {
  // index = alias.indexOf(',');
  // if (index != -1) {
  // alias = alias.substring(0, index).trim();
  // }
  // }
  // }
  // }
  // }
  //
  // int hashIndex = name.lastIndexOf('#');
  //
  // String hash = "";
  // if (-1 != hashIndex && hashIndex != name.length() - 1) {
  // hash = name.substring(hashIndex + 1);
  // name = name.substring(0, hashIndex);
  // }
  //
  // // trim the name for whitespace characters on the right side
  // int trimRightIndex = name.length() - 1;
  // while ((trimRightIndex >= 0) && (name.charAt(trimRightIndex) <= ' ')) {
  // trimRightIndex--;
  // }
  // if (trimRightIndex != name.length() - 1) {
  // name = name.substring(0, trimRightIndex + 1);
  // }
  //
  // name = Encoder.encodeHtml(name);
  // String view;
  // if (-1 != pipeIndex) {
  // view = alias + suffix;
  // } else {
  // if (name.length() > 0 && name.charAt(0) == ':') {
  // view = name.substring(1) + suffix;
  // } else {
  // view = name + suffix;
  // }
  // }
  //
  // if (handleNamespaceLinks(name, view, pipeIndex)) {
  // return;
  // }
  //
  // int indx = name.indexOf(':');
  // String namespace = null;
  // if (indx >= 0) {
  // namespace = name.substring(0, indx);
  // }
  // if (namespace != null && fWikiModel.isImageNamespace(namespace)) {
  // fWikiModel.parseInternalImageLink(namespace, linkText);
  // return;
  // } else {
  // if (name.length() > 0 && name.charAt(0) == ':') {
  // name = name.substring(1);
  // }
  // if (name.length() > 0 && name.charAt(0) == ':') {
  // name = name.substring(1);
  // }
  // fWikiModel.addLink(name);
  // if (-1 != hashIndex) {
  // fWikiModel.appendInternalLink(name, hash, view);
  // } else {
  // fWikiModel.appendInternalLink(name, null, view);
  // }
  // }
  // }
  // }
  /**
   * @param name
   * @param view
   */
  // private boolean handleNamespaceLinks(String name, String view, int
  // pipeIndex) {
  // int colonIndex = name.indexOf(':');
  //
  // if (colonIndex != (-1)) {
  // String nameSpace = name.substring(0, colonIndex);
  //
  // if (fWikiModel.isSemanticWebActive() && (name.length() > colonIndex + 1)) {
  // // See <a
  // // href="http://en.wikipedia.org/wiki/Semantic_MediaWiki">Semantic
  // // MediaWiki</a> for more information.
  // if (name.charAt(colonIndex + 1) == ':') {
  // // found an SMW relation
  // String relationValue = name.substring(colonIndex + 2);
  //
  // if (fWikiModel.addSemanticRelation(nameSpace, relationValue)) {
  // if ((-1) == pipeIndex) {
  // view = relationValue;
  // }
  // if (view.trim().length() > 0) {
  // fWikiModel.appendInternalLink(relationValue, null, view);
  // }
  // return true;
  // }
  // } else if (name.charAt(colonIndex + 1) == '=') {
  // // found an SMW attribute
  // String attributeValue = name.substring(colonIndex + 2);
  // if (fWikiModel.addSemanticAttribute(nameSpace, attributeValue)) {
  // fWikiModel.append(new ContentToken(attributeValue));
  // return true;
  // }
  // }
  //
  // }
  // if (fWikiModel.isCategoryNamespace(nameSpace)) {
  // // add the category to this texts metadata
  // String category = name.substring(colonIndex + 1);
  // if (category != null && category.length() > 0) {
  // fWikiModel.addCategory(category, "");
  // return true;
  // }
  // } else if (fWikiModel.isInterWiki(nameSpace)) {
  // String title = name.substring(colonIndex + 1);
  // if (title != null && title.length() > 0) {
  // fWikiModel.appendInterWikiLink(nameSpace, title, view);
  // return true;
  // }
  // }
  // }
  // return false;
  // }
  // private void addToTableOfContent(List<Object> toc, String head, String
  // anchor, int headLevel) {
  // if (headLevel == 1) {
  // toc.add(new StringPair(head, anchor));
  // } else {
  // if (toc.size() > 0) {
  // if (toc.get(toc.size() - 1) instanceof List) {
  // addToTableOfContent((List<Object>) toc.get(toc.size() - 1), head, anchor,
  // --headLevel);
  // return;
  // }
  // }
  // ArrayList<Object> list = new ArrayList<Object>();
  // toc.add(list);
  // addToTableOfContent(list, head, anchor, --headLevel);
  // }
  // }
  /**
   * handle head for table of content
   *
   * @param head
   * @param headLevel
   */
  private void handleHead(String rawHead, int headLevel) {
    if (rawHead != null) {
      fWikiModel.appendHead(rawHead, headLevel, fNoToC, ++fHeadCounter);

      // String anchor = Encoder.encodeUrl(head.trim());
      //
      // createTableOfContent(false);
      // if (!fNoToC && (++fHeadCounter) > 3) {
      // fTableOfContentTag.setShowToC(true);
      // }
      // if (fToCSet.contains(anchor)) {
      // String newAnchor = anchor;
      // for (int i = 2; i < Integer.MAX_VALUE; i++) {
      // newAnchor = anchor + '_' + Integer.toString(i);
      // if (!fToCSet.contains(newAnchor)) {
      // break;
      // }
      // }
      // anchor = newAnchor;
      // }
      // addToTableOfContent(fTableOfContent, head, anchor, headLevel);
      // if (fWikiModel.getRecursionLevel() == 1) {
      // fWikiModel.buildEditLinkUrl(fSectionCounter++);
      // }
      // TagNode aTagNode = new TagNode("a");
      // aTagNode.addAttribute("name", anchor, true);
      // aTagNode.addAttribute("id", anchor, true);
      // fWikiModel.append(aTagNode);
      // WPTag headTagNode = new WPTag("h" + headLevel);
      // headTagNode.addChild(new ContentToken(head));
      // fWikiModel.append(headTagNode);

    }
  }

  /**
   *
   * @param isTOCIdentifier
   *          <code>true</code> if the __TOC__ keyword was parsed
   */
  // private void createTableOfContent(boolean isTOCIdentifier) {
  // fTableOfContentTag = fWikiModel.getTableOfContentTag(isTOCIdentifier);
  // if (fTableOfContentTag != null) {
  // if (fTableOfContent == null) {
  // fTableOfContent = fTableOfContentTag.getTableOfContent();
  // }
  // }
  // fToCSet = new HashSet<String>();
  // }
  private void handleTag(TagToken tag, WikiTagNode tagNode, String bodyString) {
    String command = tagNode.getTagName();
    try {
      if (tag instanceof EndTagToken) {
        fWikiModel.append(tag);
      } else {
        fWikiModel.pushNode(tag);
        if (null != bodyString) {
          if (tag instanceof INoBodyParsingTag) {
            ((TagNode) tag).addChild(new ContentToken(bodyString));
          } else {
            // recursively filter tags within the tags body string
            TracParser.parseRecursive(bodyString.trim(), fWikiModel, false, true);
          }
        }
        if (tag instanceof IBodyTag) {
          fWikiModel.popNode();
        }
      }
    } catch (IllegalArgumentException e) {
      TagNode divTagNode = new TagNode("div");
      divTagNode.addAttribute("class", "error", true);
      divTagNode.addChild(new ContentToken("IllegalArgumentException: " + command + " - " + e.getMessage()));
      fWikiModel.append(divTagNode);
      e.printStackTrace();
    } catch (Throwable e) {
      e.printStackTrace();
      TagNode divTagNode = new TagNode("div");
      divTagNode.addAttribute("class", "error", true);
      divTagNode.addChild(new ContentToken(command + ": " + e.getMessage()));
      fWikiModel.append(divTagNode);
      e.printStackTrace();
      return;
    }
  }

  public void runParser() {
    int token = TokenSTART;
    while ((token = getNextToken()) != TokenEOF) {
      switch (token) {
      case TokenBOLDITALIC:
        if (fWikiModel.stackSize() > 0 && fWikiModel.peekNode().equals(BOLDITALIC)) {
          fWikiModel.popNode();
          // fResultBuffer.append("</i></b>");
        } else if (fWikiModel.stackSize() > 1 && fWikiModel.peekNode().equals(BOLD)
            && fWikiModel.getNode(fWikiModel.stackSize() - 2).equals(ITALIC)) {
          fWikiModel.popNode();
          fWikiModel.popNode();
          // fResultBuffer.append("</b></i>");
        } else if (fWikiModel.stackSize() > 1 && fWikiModel.peekNode().equals(ITALIC)
            && fWikiModel.getNode(fWikiModel.stackSize() - 2).equals(BOLD)) {
          fWikiModel.popNode();
          fWikiModel.popNode();
          // fResultBuffer.append("</i></b>");
        } else if (fWikiModel.stackSize() > 0 && fWikiModel.peekNode().equals(BOLD)) {
          fWikiModel.popNode();
          fWikiModel.pushNode(new WPTag("i"));
        } else if (fWikiModel.stackSize() > 0 && fWikiModel.peekNode().equals(ITALIC)) {
          fWikiModel.popNode();
          fWikiModel.pushNode(new WPTag("b"));
        } else {
          fWikiModel.pushNode(new WPBoldItalicTag());
          // fResultBuffer.append("<b><i>");
        }
        break;
      case TokenBOLD:
        if (fWikiModel.stackSize() > 0 && fWikiModel.peekNode().equals(BOLDITALIC)) {
          fWikiModel.popNode();
          fWikiModel.pushNode(new WPTag("i"));
          // fResultBuffer.append("</b>");
        } else if (fWikiModel.stackSize() > 0 && fWikiModel.peekNode().equals(BOLD)) {
          fWikiModel.popNode();
          // fResultBuffer.append("</b>");
        } else {
          fWikiModel.pushNode(new WPTag("b"));
          // fResultBuffer.append("<b>");
        }
        break;
      case TokenITALIC:
        if (fWikiModel.stackSize() > 0 && fWikiModel.peekNode().equals(BOLDITALIC)) {
          fWikiModel.popNode();
          fWikiModel.pushNode(new WPTag("b"));
          // fResultBuffer.append("</i>");
        } else if (fWikiModel.stackSize() > 0 && fWikiModel.peekNode().equals(ITALIC)) {
          fWikiModel.popNode();
          // fResultBuffer.append("</i>");
        } else {
          fWikiModel.pushNode(new WPTag("i"));
          // fResultBuffer.append("<i>");
        }
        break;
      }
    }
    reduceTokenStack();

    if (!fNoToC && fTableOfContentTag != null) {
      if (fHeadCounter > 3 || fForceToC) {
        fTableOfContentTag.setShowToC(true);
      }
    }

  }

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

  /**
   * Reduce the current token stack until an allowed parent is at the top of the
   * stack
   */
  private 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();
      }
    }
  }

  /**
   * Reduce the current token stack until the given nodes name is at the top of
   * the stack. Useful for closing HTML tags.
   */
  private void reduceStackUntilToken(TagToken node) {
    TagToken tag;
    int index = -1;
    String allowedParents = node.getParents();
    while (fWikiModel.stackSize() > 0) {
      tag = fWikiModel.peekNode();
      if (node.getName().equals(tag.getName())) {
        fWikiModel.popNode();
        break;
      }
      if (allowedParents == null) {
        fWikiModel.popNode();
      } else {
        index = allowedParents.indexOf("|" + tag.getName() + "|");
        if (index < 0) {
          fWikiModel.popNode();
        } else {
          break;
        }
      }
    }
  }

  /**
   * count the number of wiki headers in this document
   *
   * @param toc
   * @return
   */
  // private int isToC(List<Object> toc) {
  // if (toc == null) {
  // return 0;
  // }
  // if (toc.size() == 1 && (toc.get(0) instanceof List)) {
  // return isToC((List<Object>) toc.get(0));
  // }
  // int result = 0;
  // for (int i = 0; i < toc.size(); i++) {
  // if (toc.get(i) instanceof List) {
  // result += isToC((List<Object>) toc.get(i));
  // } else {
  // result++;
  // }
  // }
  // return result;
  // }
  public boolean isNoToC() {
    return fNoToC;
  }

  public void setNoToC(boolean noToC) {
    fNoToC = noToC;
  }

  /**
   * Call the parser on the first recursion level, where the text can contain a
   * table of contents (TOC).
   *
   * <br/><br/><b>Note:</b> in this level the wiki model will call the
   * <code>setUp()</code> method before parsing and the <code>tearDown()</code>
   * method after the parser has finished.
   *
   * @param wikiModel
   * @param rawWikitext
   *
   * @return
   */
  public static void parse(String rawWikiText, IWikiModel wikiModel) {
    try {
      // initialize the wiki model
      wikiModel.setUp();

      StringBuilder buf = new StringBuilder(rawWikiText.length() + rawWikiText.length() / 10);
      // if (parseTemplates) {
      // TemplateParser.parse(rawWikiText, wikiModel, buf);
      // String pass1Text = buf.toString();
      // if (!parseRedirect(pass1Text, wikiModel)) {
      // parseRecursive(pass1Text, wikiModel, false, false);
      // }
      // } else {
      // if (!parseRedirect(rawWikiText, wikiModel)) {
      parseRecursive(rawWikiText, wikiModel, false, false);
      // }
      // }
    } finally {
      // clean up wiki model if necessary
      wikiModel.tearDown();
    }
  }

  /**
   * Check the text for a <code>#REDIRECT [[...]]</code> or
   * <code>#redirect [[...]]</code> link
   *
   * @param rawWikiText
   *          the wiki text
   * @param wikiModel
   * @return <code>true</code> if a redirect was found and further parsing
   *         should be canceled according to the model.
   */
  // private static boolean parseRedirect(String rawWikiText, IWikiModel
  // wikiModel) {
  // int redirectStart = -1;
  // int redirectEnd = -1;
  // for (int i = 0; i < rawWikiText.length(); i++) {
  // if (rawWikiText.charAt(i) == '#') {
  // boolean isRedirect = rawWikiText.startsWith("redirect", i + 1);
  // if (!isRedirect) {
  // isRedirect = rawWikiText.startsWith("REDIRECT", i + 1);
  // }
  // if (isRedirect) {
  // 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 &&
  // wikiModel.appendRedirectLink(rawWikiText.substring(redirectStart,
  // redirectEnd))) {
  // return true;
  // }
  // return false;
  // }
  /**
   * Call the parser on the subsequent recursion levels, where the subtexts (of
   * templates, table cells, list items or image captions) don't contain a table
   * of contents (TOC)
   *
   * <b>Note:</b> the wiki model doesn't call the <code>setUp()</code> or
   * <code>tearDown()</code> methods for the subsequent recursive parser steps.
   *
   * @param rawWikitext
   * @param wikiModel
   * @return
   */
  public static void parseRecursive(String rawWikitext, IWikiModel wikiModel) {
    parseRecursive(rawWikitext, wikiModel, false, true);
  }

  /**
   * Call the parser on the subsequent recursion levels, where the subtexts (of
   * templates, table cells, list items or image captions) don't contain a table
   * of contents (TOC)
   *
   * <b>Note:</b> the wiki model doesn't call the <code>setUp()</code> or
   * <code>tearDown()</code> methods for the subsequent recursive parser steps.
   *
   * @param rawWikitext
   * @param wikiModel
   * @param noTOC
   * @param appendStack
   * @return
   * @return
   */
  public static TagStack parseRecursive(String rawWikitext, IWikiModel wikiModel, boolean createOnlyLocalStack, boolean noTOC) {
    AbstractParser parser = wikiModel.createNewInstance(rawWikitext);
    return parser.parseRecursiveInternal(wikiModel, createOnlyLocalStack, noTOC);
    //
    //   
    // // local stack for this wiki snippet
    // TagStack localStack = new TagStack();
    // // global wiki model stack
    // TagStack globalWikiModelStack = wikiModel.swapStack(localStack);
    // try {
    // int level = wikiModel.incrementRecursionLevel();
    //
    // if (level > TracParser.RECURSION_LIMIT) {
    // TagNode error = new TagNode("span");
    // error.addAttribute("class", "error");
    // error.addChild(new ContentToken("Error - recursion limit exceeded parsin
    // wiki tags."));
    // localStack.append(error);
    // return localStack;
    // }
    // TracParser parser = new TracParser(rawWikitext,
    // wikiModel.getWikiListener());
    // parser.setModel(wikiModel);
    // parser.setNoToC(noTOC);
    // parser.runParser();
    // return localStack;
    // } catch (Exception e) {
    // e.printStackTrace();
    // TagNode error = new TagNode("span");
    // error.addAttribute("class", "error");
    // error.addChild(new ContentToken(e.getClass().getSimpleName()));
    // localStack.append(error);
    // } catch (Error e) {
    // e.printStackTrace();
    // TagNode error = new TagNode("span");
    // error.addAttribute("class", "error");
    // error.addChild(new ContentToken(e.getClass().getSimpleName()));
    // localStack.append(error);
    // } finally {
    // wikiModel.decrementRecursionLevel();
    // if (!createOnlyLocalStack) {
    // // append the resursively parsed local stack to the global wiki
    // // model
    // // stack
    // globalWikiModelStack.append(localStack);
    // }
    // wikiModel.swapStack(globalWikiModelStack);
    // }
    //
    // return localStack;
  }

  public boolean isTemplate() {
    return fRenderTemplate;
  }

  // public static void addCustomHTMLTag(String wikiTag, HTMLTag token) {
  // WikipediaParser.TAGS_MAP.put(wikiTag, token);
  // }
  //
  // public static void addCustomHTMLTag(String wikiTag, HTMLEndTag token) {
  // WikipediaParser.TAGS_MAP.put(wikiTag, token);
  // }

}
TOP

Related Classes of info.bliki.wiki.addon.trac.TracParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.