initial contribution
[phpeclipse.git] / archive / org.plog4u.wiki / src / org / plog4u / wiki / filter / WikipediaParser.java
diff --git a/archive/org.plog4u.wiki/src/org/plog4u/wiki/filter/WikipediaParser.java b/archive/org.plog4u.wiki/src/org/plog4u/wiki/filter/WikipediaParser.java
new file mode 100644 (file)
index 0000000..5383fb7
--- /dev/null
@@ -0,0 +1,2739 @@
+package org.plog4u.wiki.filter;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
+import java.util.Stack;
+import java.util.StringTokenizer;
+
+//import org.apache.commons.logging.Log;
+//import org.apache.commons.logging.LogFactory;
+import org.plog4u.wiki.filter.WikipediaFilter.InvalidInputException;
+import org.plog4u.wiki.filter.tags.AbstractTag;
+import org.plog4u.wiki.filter.tags.CloseTagToken;
+import org.plog4u.wiki.filter.tags.ListToken;
+import org.plog4u.wiki.filter.tags.OpenTagToken;
+import org.plog4u.wiki.filter.tags.SpecialTagToken;
+import org.radeox.api.engine.ImageRenderEngine;
+import org.radeox.api.engine.IncludeRenderEngine;
+import org.radeox.api.engine.RenderEngine;
+import org.radeox.api.engine.WikiRenderEngine;
+import org.radeox.filter.context.FilterContext;
+import org.radeox.filter.interwiki.InterWiki;
+import org.radeox.macro.Macro;
+import org.radeox.macro.MacroRepository;
+import org.radeox.macro.parameter.MacroParameter;
+import org.radeox.util.Encoder;
+import org.radeox.util.StringBufferWriter;
+
+/**
+ * A parser for the WikipediaFilter
+ * 
+ * @see org.plog4u.wiki.filter.WikipediaFilter
+ */
+public class WikipediaParser {
+  //  private static Log log = LogFactory.getLog(WikipediaFilter.class);
+
+  MacroRepository fMacros;
+
+  private FilterContext fContext;
+
+  private RenderEngine fWikiEngine;
+
+  // TODO check, if this counter is correct in recursions:
+  private int fImageCounter;
+
+  /**
+   * The current snip
+   */
+  //  private Snip fSnip;
+  /**
+   * If the snip contains headings for a "table of content" this buffer temporarily contains the start of the snip and the
+   * "table of content"
+   */
+  private StringBuffer fResultBufferHeader = null;
+
+  /**
+   * The buffer for the resulting HTML rendering from the current snip.
+   */
+  private StringBuffer fResultBuffer;
+
+  /**
+   * The wiki syntax string which should be parsed
+   */
+  private char[] fSource;
+
+  /**
+   * The corresponding String for the character source array
+   */
+  private final String fStringSource;
+
+  /**
+   * The current scanned character
+   */
+  private char fCurrentCharacter;
+
+  /**
+   * The current offset in the character source array
+   */
+  private int fCurrentPosition;
+
+  /**
+   * The current recursion level for this parser
+   */
+  private int fRecursionLevel;
+
+  private Stack fTokenStack;
+
+  //  private Stack fTableStack;
+
+  private boolean fWhiteStart = false;
+
+  private int fWhiteStartPosition = 0;
+
+  //  private TeXParser fTeXParser;
+  //  private TeXParser fTeXImageParser;
+  /**
+   * 
+   * "table of content"
+   *  
+   */
+  private ArrayList fTableOfContent = null;
+
+  //  private String fSrcPath;
+  //  private String fBinPath;
+
+  public WikipediaParser(MacroRepository macros, String stringSource, StringBuffer result, FilterContext context, int recursionLevel) {
+    fContext = context;
+    fWikiEngine = context.getRenderContext().getRenderEngine();
+
+    //    try {
+    //      SnipMacroParameter params = (SnipMacroParameter)
+    // fContext.getMacroParameter();
+    //      fSnip = params.getSnipRenderContext().getSnip();
+    //    } catch (ClassCastException e) {
+    //      e.printStackTrace();
+    //    }
+    fMacros = macros;
+    fResultBuffer = result;
+    fStringSource = stringSource;
+    setSource(stringSource.toCharArray());
+    fRecursionLevel = recursionLevel;
+    fTokenStack = new Stack();
+    //    fTableStack = new Stack();
+    //    fTeXParser = new TeXParser("", "m:");
+    //    fTeXImageParser = new TeXParser("", "");
+    fImageCounter = 1;
+
+    //    fSrcPath = (String) fContext.getRenderContext().get("srcpath");
+    //    if (fSrcPath==null) {
+    //      fSrcPath = "";
+    //    }
+    //    fBinPath = (String) fContext.getRenderContext().get("binpath");
+    //    if (fBinPath==null) {
+    //      fBinPath = "";
+    //    }
+  }
+
+  /**
+   * Check until a new-line was found, if there are only whitespace characters before the given endposition.
+   * 
+   * @param startPosition
+   * @param endPosition
+   * @return -1 if no whitespace line is found from the end (i.e. endPosition); otherwise the offset directly after where the
+   *         new-line was found
+   */
+  private int checkWhitespaces(int startPosition, int endPosition) {
+    char tempChar;
+    while (endPosition >= startPosition) {
+      if ((tempChar = fSource[endPosition--]) == '\n') {
+        return endPosition + 2;
+      }
+      if (tempChar != ' ' && tempChar != '\t' && tempChar != '\r') {
+        return -1;
+      }
+    }
+    if (endPosition < startPosition && endPosition >= 0) {
+      if ((tempChar = fSource[endPosition]) != '\n') {
+        return -1;
+      }
+    } else if (endPosition == (-1) && startPosition == 0) {
+      // special case at the start of a string
+      return 0;
+    }
+    return startPosition;
+  }
+
+  /**
+   * copy the content in the resulting buffer and escape special html characters (&lt; &gt; &quot; &amp; &#39;)
+   */
+  private void copyWhite(boolean whiteStart, final int whiteStartPosition, final int diff) {
+    if (whiteStart) {
+      final int len = fCurrentPosition - diff;
+      int currentIndex = whiteStartPosition;
+      int lastIndex = currentIndex;
+      while (currentIndex < len) {
+        switch (fSource[currentIndex++]) {
+        case '<': // special html escape character
+          if (lastIndex < (currentIndex - 1)) {
+            fResultBuffer.append(fSource, lastIndex, currentIndex - lastIndex - 1);
+            lastIndex = currentIndex;
+          } else {
+            lastIndex++;
+          }
+          fResultBuffer.append("&#60;");
+          break;
+        case '>': // special html escape character
+          if (lastIndex < (currentIndex - 1)) {
+            fResultBuffer.append(fSource, lastIndex, currentIndex - lastIndex - 1);
+            lastIndex = currentIndex;
+          } else {
+            lastIndex++;
+          }
+          fResultBuffer.append("&#62;");
+          break;
+        case '&': // special html escape character
+          if (lastIndex < (currentIndex - 1)) {
+            fResultBuffer.append(fSource, lastIndex, currentIndex - lastIndex - 1);
+            lastIndex = currentIndex;
+          } else {
+            lastIndex++;
+          }
+          fResultBuffer.append("&#38;");
+          break;
+        case '\'': // special html escape character
+          if (lastIndex < (currentIndex - 1)) {
+            fResultBuffer.append(fSource, lastIndex, currentIndex - lastIndex - 1);
+            lastIndex = currentIndex;
+          } else {
+            lastIndex++;
+          }
+          fResultBuffer.append("&#39;");
+          break;
+        case '\"': // special html escape character
+          if (lastIndex < (currentIndex - 1)) {
+            fResultBuffer.append(fSource, lastIndex, currentIndex - lastIndex - 1);
+            lastIndex = currentIndex;
+          } else {
+            lastIndex++;
+          }
+          fResultBuffer.append("&#34;");
+          break;
+        }
+      }
+      if (lastIndex < (currentIndex)) {
+        fResultBuffer.append(fSource, lastIndex, currentIndex - lastIndex);
+      }
+      fWhiteStart = false;
+    }
+  }
+
+  /**
+   * copy the text in the resulting buffer and escape special html characters (&lt; &gt; &quot; &amp; &#39;)
+   */
+  private void copyWhite(String text) {
+    final int len = text.length();
+    int currentIndex = 0;
+    int lastIndex = currentIndex;
+    while (currentIndex < len) {
+      switch (text.charAt(currentIndex++)) {
+      case '<': // special html escape character
+        if (lastIndex < (currentIndex - 1)) {
+          fResultBuffer.append(text.substring(lastIndex, currentIndex - 1));
+          lastIndex = currentIndex;
+        }
+        fResultBuffer.append("&#60;");
+        break;
+      case '>': // special html escape character
+        if (lastIndex < (currentIndex - 1)) {
+          fResultBuffer.append(text.substring(lastIndex, currentIndex - 1));
+          lastIndex = currentIndex;
+        } else {
+          lastIndex++;
+        }
+        fResultBuffer.append("&#62;");
+        break;
+      case '&': // special html escape character
+        if (lastIndex < (currentIndex - 1)) {
+          fResultBuffer.append(text.substring(lastIndex, currentIndex - 1));
+          lastIndex = currentIndex;
+        } else {
+          lastIndex++;
+        }
+        fResultBuffer.append("&#38;");
+        break;
+      case '\'': // special html escape character
+        if (lastIndex < (currentIndex - 1)) {
+          fResultBuffer.append(text.substring(lastIndex, currentIndex - 1));
+          lastIndex = currentIndex;
+        } else {
+          lastIndex++;
+        }
+        fResultBuffer.append("&#39;");
+        break;
+      case '\"': // special html escape character
+        if (lastIndex < (currentIndex - 1)) {
+          fResultBuffer.append(text.substring(lastIndex, currentIndex - 1));
+          lastIndex = currentIndex;
+        } else {
+          lastIndex++;
+        }
+        fResultBuffer.append("&#34;");
+        break;
+      }
+    }
+    if (lastIndex < (currentIndex)) {
+      fResultBuffer.append(text.substring(lastIndex, currentIndex));
+    }
+  }
+
+  /**
+   * Copy the text in the resulting buffer and escape special html characters (&lt; &gt; &quot; &amp; &#39;) Additionally every
+   * newline will be replaced by &lt;br/&gt;
+   */
+  private void copyNowikiNewLine(String text) {
+    final int len = text.length();
+    int currentIndex = 0;
+    int lastIndex = currentIndex;
+    while (currentIndex < len) {
+      switch (text.charAt(currentIndex++)) {
+      case '\n':
+        if (lastIndex < (currentIndex - 1)) {
+          fResultBuffer.append(text.substring(lastIndex, currentIndex - 1));
+          lastIndex = currentIndex;
+        } else {
+          lastIndex++;
+        }
+        fResultBuffer.append("<br/>");
+        break;
+      case '<': // special html escape character
+        if (lastIndex < (currentIndex - 1)) {
+          fResultBuffer.append(text.substring(lastIndex, currentIndex - 1));
+          lastIndex = currentIndex;
+        } else {
+          lastIndex++;
+        }
+        fResultBuffer.append("&#60;");
+        break;
+      case '>': // special html escape character
+        if (lastIndex < (currentIndex - 1)) {
+          fResultBuffer.append(text.substring(lastIndex, currentIndex - 1));
+          lastIndex = currentIndex;
+        } else {
+          lastIndex++;
+        }
+        fResultBuffer.append("&#62;");
+        break;
+      //      case '&': // special html escape character
+      //        if (lastIndex < (currentIndex - 1)) {
+      //          fResultBuffer.append(text.substring(lastIndex, currentIndex - 1));
+      //          lastIndex = currentIndex;
+      //        } else {
+      //          lastIndex++;
+      //        }
+      //        fResultBuffer.append("&#38;");
+      //        break;
+      case '\'': // special html escape character
+        if (lastIndex < (currentIndex - 1)) {
+          fResultBuffer.append(text.substring(lastIndex, currentIndex - 1));
+          lastIndex = currentIndex;
+        } else {
+          lastIndex++;
+        }
+        fResultBuffer.append("&#39;");
+        break;
+      case '\"': // special html escape character
+        if (lastIndex < (currentIndex - 1)) {
+          fResultBuffer.append(text.substring(lastIndex, currentIndex - 1));
+          lastIndex = currentIndex;
+        } else {
+          lastIndex++;
+        }
+        fResultBuffer.append("&#34;");
+        break;
+      }
+    }
+    if (lastIndex < (currentIndex)) {
+      fResultBuffer.append(text.substring(lastIndex, currentIndex));
+    }
+  }
+
+  /**
+   * Render the HTML token which are defined in the OPEN_TAGS and CLOSE_TAGS map
+   * 
+   * @return
+   */
+  public int getHTMLToken() {
+    int currentHtmlPosition = fCurrentPosition;
+    try {
+      char closeCharacter;
+      char nextCharacter;
+      if (getNextChar('/')) {
+        currentHtmlPosition++;
+        // closing tag
+        int r = readUntilCharOrEOL('>');
+        if (r != 1) {
+          return WikipediaFilter.TokenNotFound;
+        }
+        String closeTagString = new String(fSource, currentHtmlPosition, fCurrentPosition - currentHtmlPosition - 1).toLowerCase();
+        //          System.out.println(closeTagString);
+        StringTokenizer tagTokenizer = new StringTokenizer(closeTagString);
+        String tokenString;
+        try {
+          tokenString = tagTokenizer.nextToken();
+          CloseTagToken token = (CloseTagToken) WikipediaFilter.CLOSE_TAGS.get(tokenString);
+          if (token == null) {
+            return WikipediaFilter.TokenNotFound;
+          }
+          Object topToken = fTokenStack.peek();
+          if (topToken instanceof OpenTagToken && ((OpenTagToken) topToken).getTagName() == token.getTagName()) {
+            fTokenStack.pop();
+            //            if (token.getTagName().equals("table")) {
+            //              fTableStack.pop();
+            //            }
+            copyWhite(fWhiteStart, fWhiteStartPosition, 3 + tokenString.length());
+            fWhiteStart = false;
+            fResultBuffer.append(token.getCloseTag());
+            return WikipediaFilter.TokenIgnore;
+          }
+          fWhiteStart = false;
+          unexpectedTag(token.getTagName());
+          return WikipediaFilter.TokenIgnore;
+        } catch (NoSuchElementException e) {
+          return WikipediaFilter.TokenNotFound;
+        }
+
+      } else {
+        // opening tag
+        String tokenString;
+        int tagNameStart = fCurrentPosition;
+        int tokenLength = 0;
+        while (Character.isJavaIdentifierStart(fSource[fCurrentPosition++])) {
+          tokenLength++;
+        }
+        try {
+          tokenString = new String(fSource, tagNameStart, fCurrentPosition - tagNameStart - 1); //tagTokenizer.nextToken();
+          OpenTagToken token = (OpenTagToken) WikipediaFilter.OPEN_TAGS.get(tokenString);
+          if (token == null) {
+            return WikipediaFilter.TokenNotFound;
+          }
+          copyWhite(fWhiteStart, fWhiteStartPosition, (fCurrentPosition - tagNameStart) + 1);
+          fWhiteStart = false;
+
+          if (token instanceof SpecialTagToken) {
+            fResultBuffer.append(token.getOpenTag());
+            while (Character.isWhitespace(fSource[fCurrentPosition])) {
+              fCurrentPosition++;
+            }
+            if (fSource[fCurrentPosition] == '/') {
+              fCurrentPosition++;
+            }
+            if (fSource[fCurrentPosition] == '>') {
+              fCurrentPosition++;
+            }
+          } else if (token instanceof OpenTagToken) {
+            fResultBuffer.append("<");
+            fResultBuffer.append(token.getTagName());
+            fTokenStack.push(token);
+            fCurrentPosition = token.scan(fResultBuffer, fSource, fCurrentPosition - 1);
+            fResultBuffer.append(">");
+          }
+
+          //                                   System.out.println(fResultBuffer);
+          return WikipediaFilter.TokenIgnore;
+        } catch (NoSuchElementException e) {
+          return WikipediaFilter.TokenNotFound;
+        }
+      }
+
+    } catch (IndexOutOfBoundsException e) {
+      //
+    }
+    fCurrentPosition = currentHtmlPosition;
+    return WikipediaFilter.TokenNotFound;
+  }
+
+  public 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;
+    }
+  }
+
+  public 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;
+    }
+  }
+
+  public 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;
+    }
+  }
+
+  public 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;
+    }
+  }
+
+  public final int getNumberOfChar(char testedChar) {
+    int number = 0;
+    try {
+      while ((fCurrentCharacter = fSource[fCurrentPosition++]) == testedChar) {
+        number++;
+      }
+    } catch (IndexOutOfBoundsException e) {
+
+    }
+    fCurrentPosition--;
+    return number;
+  }
+
+  public final char[] getListChars() {
+
+    int startPosition = fCurrentPosition - 1;
+    try {
+      while (true) {
+        fCurrentCharacter = fSource[fCurrentPosition++];
+        if (fCurrentCharacter != '*' && fCurrentCharacter != '#') {
+          break;
+        }
+      }
+    } catch (IndexOutOfBoundsException e) {
+      //
+    }
+    fCurrentPosition--;
+    char[] result = new char[fCurrentPosition - startPosition];
+    System.arraycopy(fSource, startPosition, result, 0, fCurrentPosition - startPosition);
+    return result;
+  }
+
+  public boolean getNextCharAsWikiPluginIdentifierPart() {
+    int temp = fCurrentPosition;
+    try {
+      fCurrentCharacter = fSource[fCurrentPosition++];
+
+      if (!WikipediaFilter.isWikiPluginIdentifierPart(fCurrentCharacter)) {
+        fCurrentPosition = temp;
+        return false;
+      }
+      return true;
+    } catch (IndexOutOfBoundsException e) {
+      fCurrentPosition = temp;
+      return false;
+    }
+  }
+
+  private void stopList() {
+    while (!fTokenStack.isEmpty()) {
+      AbstractTag tok = (AbstractTag) fTokenStack.peek();
+      if (tok.equals(WikipediaFilter.LIST_UL_START)) {
+        fTokenStack.pop();
+        fResultBuffer.append("</li></ul>");
+      } else if (tok.equals(WikipediaFilter.LIST_OL_START)) {
+        fTokenStack.pop();
+        fResultBuffer.append("</li></ol>");
+      } else if (tok == WikipediaFilter.BOLD) {
+        fTokenStack.pop();
+        fResultBuffer.append("</b>");
+      } else if (tok == WikipediaFilter.ITALIC) {
+        fTokenStack.pop();
+        fResultBuffer.append("</i>");
+      } else if (tok == WikipediaFilter.STRONG) {
+        fTokenStack.pop();
+        fResultBuffer.append("</strong>");
+      } else if (tok == WikipediaFilter.EM) {
+        fTokenStack.pop();
+        fResultBuffer.append("</em>");
+      } else if (tok == WikipediaFilter.STRIKETHROUGH) {
+        fTokenStack.pop();
+        fResultBuffer.append("</strike>");
+      } else {
+        break;
+      }
+    }
+  }
+
+  protected int getNextToken() throws InvalidInputException {
+    boolean startOfIndent = false;
+    fWhiteStartPosition = 0;
+    fWhiteStart = false;
+    try {
+      while (true) {
+        //          fStartPosition = fCurrentPosition;
+        fCurrentCharacter = fSource[fCurrentPosition++];
+
+        // ---------Identify the next token-------------
+        switch (fCurrentCharacter) {
+        case '\n':
+          if (fWhiteStart) {
+            int tempPosition = checkWhitespaces(fWhiteStartPosition, fCurrentPosition - 2);
+            if (tempPosition >= 0) {
+              copyWhite(fWhiteStart, fWhiteStartPosition, fCurrentPosition - (++tempPosition));
+              fWhiteStart = false;
+              stopList();
+              fResultBuffer.append("<p/>");
+              //                                                       continue;
+            }
+
+          }
+          int fStartPrePosition = fCurrentPosition;
+          boolean preSection = false;
+          try {
+            while (fSource[fCurrentPosition++] == ' ') {
+              fCurrentCharacter = fSource[fCurrentPosition++];
+              while (fCurrentCharacter != '\n') {
+                if (!Character.isWhitespace(fCurrentCharacter)) {
+                  //                                                                    preformatted section starts here
+                  preSection = true;
+                }
+                fCurrentCharacter = fSource[fCurrentPosition++];
+              }
+            }
+            --fCurrentPosition;
+          } catch (IndexOutOfBoundsException e) {
+
+          }
+          if (preSection && fRecursionLevel == 1) {
+            String preString;
+            copyWhite(fWhiteStart, fStartPrePosition, fCurrentPosition - fStartPrePosition);
+            fWhiteStart = true;
+            fResultBuffer.append("<pre>");
+            //            copyWhite(fWhiteStart, fStartPrePosition, 1);
+            preString = new String(fSource, fStartPrePosition, fCurrentPosition - fStartPrePosition - 1) + '\n';
+            fResultBuffer.append(WikipediaFilter.filterParser(preString, fContext, fMacros, fRecursionLevel));
+            //            preString = new String(fSource, fStartPrePosition, fCurrentPosition - fStartPrePosition - 1)+'\n';
+            //            int preIndex = 0;
+            //            int lastIndex = 0;
+            //            while (preIndex>=0) {
+            //              preIndex = preString.indexOf('\n', lastIndex);
+            //              if (preIndex>=0) {
+            //                fResultBuffer.append(WikipediaFilter.filterParser(preString.substring(lastIndex,preIndex), fContext,
+            // fCachedPage, fMacros, fRecursionLevel));
+            //                fResultBuffer.append('\n');
+            //                lastIndex = ++preIndex;
+            //              }
+            //            }
+            fResultBuffer.append("</pre>");
+            fWhiteStart = false;
+            continue;
+          } else {
+            fCurrentPosition = fStartPrePosition;
+          }
+          break;
+        case ':':
+          if (isStartOfLine()) {
+            copyWhite(fWhiteStart, fWhiteStartPosition, 1);
+            fWhiteStart = false;
+
+            int levelHeader = getNumberOfChar(':') + 1;
+            int startHeadPosition = fCurrentPosition;
+            if (readUntilEOL()) {
+              String head = new String(fSource, startHeadPosition, fCurrentPosition - startHeadPosition);
+              for (int i = 0; i < levelHeader; i++) {
+                fResultBuffer.append("<dl><dd>");
+              }
+              fResultBuffer.append(head);
+              for (int i = 0; i < levelHeader; i++) {
+                fResultBuffer.append("</dd></dl>");
+              }
+              continue;
+            }
+
+            continue;
+          }
+          break;
+        case ';':
+          if (isStartOfLine() && getNextChar(' ')) {
+            copyWhite(fWhiteStart, fWhiteStartPosition, 1);
+            fWhiteStart = false;
+
+            int startHeadPosition = fCurrentPosition;
+            if (readUntilEOL()) {
+              // TODO not correct - improve this
+              String head = new String(fSource, startHeadPosition, fCurrentPosition - startHeadPosition);
+              int index = head.indexOf(": ");
+              if (index > 0) {
+                fResultBuffer.append("<dl><dt>");
+                fResultBuffer.append(head.substring(0,index));
+                fResultBuffer.append("</dt><dd>");
+                fResultBuffer.append(head.substring(index+2));
+                fResultBuffer.append("</dd></dl>");
+              } else {
+                fResultBuffer.append("<dl><dt>");
+                fResultBuffer.append(head);
+                fResultBuffer.append("</dt></dl>");
+              }
+              continue;
+            }
+
+            continue;
+          }
+          break;
+        //                             case '\\': // special characters follow
+        //                                     copyWhite(fWhiteStart, fWhiteStartPosition, 1);
+        //                                     fWhiteStart = false;
+        //                                     try {
+        //                                             fCurrentCharacter = fSource[fCurrentPosition++];
+        //                                             switch (fCurrentCharacter) {
+        //                                             case '\\': // newline
+        //                                                     if ((fCurrentCharacter = fSource[fCurrentPosition++]) == '\\') {
+        //                                                             fResultBuffer.append(Encoder
+        //                                                                             .toEntity(fCurrentCharacter));
+        //                                                             break;
+        //                                                     } else {
+        //                                                             fResultBuffer.append("<br />");
+        //                                                             break;
+        //                                                     }
+        //                                             default:
+        //                                                     fResultBuffer.append(Encoder
+        //                                                                     .toEntity(fCurrentCharacter));
+        //                                             }
+        //                                     } catch (IndexOutOfBoundsException e) {
+        //
+        //                                     }
+        //                                     continue;
+        //          case '$' : // detect tex math
+        //            copyWhite(fWhiteStart, fWhiteStartPosition, 1);
+        //            fWhiteStart = false;
+        //            startOfIndent = false;
+        //            int startMathPosition = fCurrentPosition;
+        //            if (getNextChar('$')) {
+        //              startMathPosition = fCurrentPosition;
+        //              copyWhite(fWhiteStart, fWhiteStartPosition, 2);
+        //              fWhiteStart = false;
+        //              if (readUntilString("$$")) {
+        //                String mathContent = new String(fSource, startMathPosition,
+        // fCurrentPosition - startMathPosition - 2);
+        //                if (mathContent != null) {
+        //                  handleTeXMath(mathContent, false);
+        //                  continue;
+        //                }
+        //              }
+        //            } else {
+        //              if (readUntilChar('$')) {
+        //                String mathContent = new String(fSource, startMathPosition,
+        // fCurrentPosition - startMathPosition - 1);
+        //                if (mathContent != null) {
+        //                  handleTeXMath(mathContent, true);
+        //                  continue;
+        //                }
+        //              }
+        //            }
+        //            break;
+        case '{':
+          // detect macros
+          copyWhite(fWhiteStart, fWhiteStartPosition, 1);
+          fWhiteStart = false;
+          //              boolean scanBody = true;
+          int startMacroPosition = fCurrentPosition;
+          if (getNextChar('|') && handleWikipediaTable()) { // Wikipedia
+            // table
+            // syntax
+            continue;
+          } else {
+            if (readUntilChar('}')) {
+              String macroStartTag;
+
+              macroStartTag = new String(fSource, startMacroPosition, fCurrentPosition - startMacroPosition - 1);
+              if (macroStartTag != null) {
+                createMacro(startMacroPosition, macroStartTag);
+                continue;
+              }
+            }
+          }
+          break;
+        case '[':
+          int startLinkPosition = fCurrentPosition;
+          if (getNextChar('[')) { // wikipedia link style
+            startLinkPosition = fCurrentPosition;
+            copyWhite(fWhiteStart, fWhiteStartPosition, 2);
+            fWhiteStart = false;
+            if (readUntilString("]]")) {
+              String name = new String(fSource, startLinkPosition, fCurrentPosition - startLinkPosition - 2);
+              // test for suffix string
+              int temp = fCurrentPosition;
+              StringBuffer suffixBuffer = new StringBuffer();
+              try {
+                while (true) {
+                  fCurrentCharacter = fSource[fCurrentPosition++];
+                  if (!Character.isLetterOrDigit(fCurrentCharacter)) {
+                    fCurrentPosition--;
+                    break;
+                  }
+                  suffixBuffer.append(fCurrentCharacter);
+                }
+                handleWikipediaLink(name, suffixBuffer.toString());
+                continue;
+              } catch (IndexOutOfBoundsException e) {
+                fCurrentPosition = temp;
+              }
+
+              handleWikipediaLink(name, "");
+              continue;
+            }
+
+          } else {
+            copyWhite(fWhiteStart, fWhiteStartPosition, 1);
+            fWhiteStart = false;
+
+            if (readUntilChar(']')) {
+              String name = new String(fSource, startLinkPosition, fCurrentPosition - startLinkPosition - 1);
+              handleSnipLink(name);
+              continue;
+            }
+          }
+          break;
+        //                             case '1': // heading filter ?
+        //                                     int temp1Position = checkWhitespaces(fWhiteStartPosition,
+        //                                                     fCurrentPosition - 2);
+        //                                     if (temp1Position >= 0) {
+        //                                             copyWhite(fWhiteStart, fWhiteStartPosition, 1);
+        //                                             fWhiteStart = false;
+        //                                             int simpleHeader = getNextChar(' ', '.');
+        //                                             if (simpleHeader < 0) {
+        //                                                     if (getNextChar('1')) {
+        //                                                             fCurrentPosition--;
+        //                                                             if (getList('1', "<ol>", "</ol>")) {
+        //                                                                     continue;
+        //                                                             }
+        //                                                     }
+        //                                                     break;
+        //                                             }
+        //                                             if (simpleHeader == 1 && !getNextChar('1')) {
+        //                                                     fCurrentPosition--;
+        //                                                     if (getList('1', "<ol>", "</ol>")) {
+        //                                                             continue;
+        //                                                     }
+        //                                                     break;
+        //                                             }
+        //                                             temp1Position = fCurrentPosition;
+        //                                             if (simpleHeader >= 0 && readUntilChar('\n')) {
+        //                                                     String heading = new String(fSource, temp1Position,
+        //                                                                     fCurrentPosition - temp1Position - 1);
+        //                                                     if (heading != null) {
+        //                                                             fResultBuffer.append("<h3 class=\"heading-");
+        //                                                             if (simpleHeader == 1) {
+        //                                                                     fResultBuffer.append("1");
+        //                                                             } else {
+        //                                                                     fResultBuffer.append("1-1");
+        //                                                             }
+        //                                                             fResultBuffer.append("\">");
+        //                                                             // System.out.println(heading);
+        //                                                             fResultBuffer
+        //                                                                             .append(WikipediaFilter
+        //                                                                                             .filterParser(
+        //                                                                                                             heading,
+        //                                                                                                             fContext,
+        //                                                                                                             WikipediaFilter.DUMMY_CACHED_PAGE,
+        //                                                                                                             fMacros,
+        //                                                                                                             fRecursionLevel));
+        //                                                             fResultBuffer.append("</h3>");
+        //                                                             continue;
+        //                                                     }
+        //                                             }
+        //                                     }
+        //                                     break;
+        case '*': // <ul> list
+        case '#': // <ol> list
+          if (isStartOfLine()) {
+            char[] listChars = getListChars();
+            int tempStarPosition = checkWhitespaces(fWhiteStartPosition, fCurrentPosition - 1 - listChars.length);
+            if (tempStarPosition >= 0) {
+              appendList(listChars);
+              continue;
+            }
+          }
+          break;
+        //        case '#': // <ol> list
+        //          if (fCurrentPosition >= 2) {
+        //            char beforeChar = fSource[fCurrentPosition - 2];
+        //            if (beforeChar == '\n' || beforeChar == '\r') {
+        //
+        //              int levelHash = getNumberOfChar('#') + 1;
+        //
+        //              int tempHashPosition = checkWhitespaces(fWhiteStartPosition, fCurrentPosition - 1 - levelHash);
+        //              if (tempHashPosition >= 0) {
+        //                copyWhite(fWhiteStart, fWhiteStartPosition, levelHash);
+        //                fWhiteStart = false;
+        //                AbstractTag tok = (AbstractTag) fTokenStack.peek();
+        //                if (tok instanceof ListToken) {
+        //                  ListToken listToken = (ListToken) tok;
+        //                  int topLevel = listToken.getLevel();
+        //                  if (listToken.getToken() == WikipediaFilter.TokenLIST_OL_START) {
+        //                    if (levelHash > topLevel) {
+        //                      fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_OL_START, topLevel + 1));
+        //                      fResultBuffer.append("<ol><li>");
+        //                    } else if (levelHash < topLevel) {
+        //                      fTokenStack.pop();
+        //                      fResultBuffer.append("</li></ol></li><li>");
+        //                    } else {
+        //                      fResultBuffer.append("</li><li>");
+        //                    }
+        //                  } else {
+        //                    fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_OL_START, levelHash));
+        //                    fResultBuffer.append("<ol><li>");
+        //                  }
+        //                } else {
+        //                  fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_OL_START, 1));
+        //                  fResultBuffer.append("\n<ol><li>");
+        //                }
+        //                continue;
+        //              }
+        //            }
+        //            // }
+        //          }
+        //          break;
+
+        //                             case 'i': // <ol> list
+        //                                     if (getList('i', "<ol class=\"roman\">", "</ol>")) {
+        //                                             continue;
+        //                                     }
+        //                                     break;
+        //                             case 'I': // <ol> list
+        //                                     if (getList('i', "<ol class=\"ROMAN\">", "</ol>")) {
+        //                                             continue;
+        //                                     }
+        //                                     break;
+        //            case 'a' : // <ol> list
+        //              if (getList('a', "<ol class=\"alpha\">", "</ol>")) {
+        //                continue;
+        //              }
+        //              break;
+        //            case 'A' : // <ol> list
+        //              if (getList('A', "<ol class=\"ALPHA\">", "</ol>")) {
+        //                continue;
+        //              }
+        //              break;
+        //            case 'g' : // <ol> list
+        //              if (getList('g', "<ol class=\"greek\">", "</ol>")) {
+        //                continue;
+        //              }
+        //              break;
+        //            case 'H' : // <ol> list
+        //              if (getList('H', "<ol class=\"HIRAGANA\">", "</ol>")) {
+        //                continue;
+        //              }
+        //              break;
+        //            case 'k' : // <ol> list
+        //              if (getList('k', "<ol class=\"katakana\">", "</ol>")) {
+        //                continue;
+        //              }
+        //              break;
+        //            case 'K' : // <ol> list
+        //              if (getList('K', "<ol class=\"KATAKANA\">", "</ol>")) {
+        //                continue;
+        //              }
+        //              break;
+        //            case 'j' : // <ol> list
+        //              if (getList('j', "<ol class=\"HEBREW\">", "</ol>")) {
+        //                continue;
+        //              }
+        //              break;
+
+        case '\'':
+          if (getNextChar('\'')) {
+            if (getNextChar('\'')) {
+              copyWhite(fWhiteStart, fWhiteStartPosition, 3);
+              fWhiteStart = false;
+              return WikipediaFilter.TokenSTRONG;
+            }
+            copyWhite(fWhiteStart, fWhiteStartPosition, 2);
+            fWhiteStart = false;
+            return WikipediaFilter.TokenEM;
+          }
+          break;
+        //                             case '_':
+        //                                     if (getNextChar('_')) {
+        //                                             copyWhite(fWhiteStart, fWhiteStartPosition, 2);
+        //                                             fWhiteStart = false;
+        //                                             return WikipediaFilter.TokenBOLD;
+        //                                     }
+        //                                     break;
+        //                             case '~':
+        //                                     if (getNextChar('~')) {
+        //                                             copyWhite(fWhiteStart, fWhiteStartPosition, 2);
+        //                                             fWhiteStart = false;
+        //                                             return WikipediaFilter.TokenITALIC;
+        //                                     }
+        //                                     break;
+        case '-':
+          int tempCurrPosition = fCurrentPosition;
+          try {
+            if (fSource[tempCurrPosition++] == '-' && fSource[tempCurrPosition++] == '-' && fSource[tempCurrPosition++] == '-') {
+              if (fSource[tempCurrPosition] == '\n') {
+                fCurrentPosition = tempCurrPosition;
+                fResultBuffer.append("<hr/>");
+                fWhiteStart = false;
+                continue;
+              } else if (fSource[tempCurrPosition++] == '\r' && fSource[tempCurrPosition++] == '\n') {
+                fCurrentPosition = tempCurrPosition - 1;
+                fResultBuffer.append("<hr/>");
+                fWhiteStart = false;
+                continue;
+              }
+            }
+          } catch (IndexOutOfBoundsException e) {
+
+          }
+
+          //                                   int levelMinus = getNumberOfChar('-') + 1;
+          //                                   if (getNextChar(' ')) {
+          //                                           int tempPosition = checkWhitespaces(
+          //                                                           fWhiteStartPosition, fCurrentPosition - 2
+          //                                                                           - levelMinus);
+          //                                           if (tempPosition >= 0) {
+          //                                                   copyWhite(fWhiteStart, fWhiteStartPosition,
+          //                                                                   1 + levelMinus);
+          //                                                   fWhiteStart = false;
+          //                                                   AbstractTag tok = (AbstractTag) fTokenStack.peek();
+          //                                                   if (tok instanceof ListToken) {
+          //                                                           ListToken listToken = (ListToken) tok;
+          //                                                           int topLevel = listToken.getLevel();
+          //                                                           if (listToken.getToken() ==
+          // WikipediaFilter.TokenLIST_UL_START) {
+          //                                                                   if (levelMinus > topLevel) {
+          //                                                                           fTokenStack
+          //                                                                                           .push(new ListToken(
+          //                                                                                                           WikipediaFilter.TokenLIST_UL_START,
+          //                                                                                                           topLevel + 1));
+          //                                                                           fResultBuffer
+          //                                                                                           .append("<ul class=\"minus\"><li>");
+          //                                                                   } else if (levelMinus < topLevel) {
+          //                                                                           fTokenStack.pop();
+          //                                                                           fResultBuffer
+          //                                                                                           .append("</li></ul></li><li>");
+          //                                                                   } else {
+          //                                                                           fResultBuffer.append("</li><li>");
+          //                                                                   }
+          //                                                           } else {
+          //                                                                   fTokenStack
+          //                                                                                   .push(new ListToken(
+          //                                                                                                   WikipediaFilter.TokenLIST_UL_START,
+          //                                                                                                   levelMinus));
+          //                                                                   fResultBuffer
+          //                                                                                   .append("<ul class=\"minus\"><li>");
+          //                                                           }
+          //                                                   } else {
+          //                                                           fTokenStack
+          //                                                                           .push(new ListToken(
+          //                                                                                           WikipediaFilter.TokenLIST_UL_START,
+          //                                                                                           1));
+          //                                                           fResultBuffer
+          //                                                                           .append("\n<ul class=\"minus\"><li>");
+          //                                                   }
+          //                                                   continue;
+          //                                           }
+          //                                   }
+          //                                   if (levelMinus == 2) {
+          //                                           copyWhite(fWhiteStart, fWhiteStartPosition, 2);
+          //                                           fWhiteStart = false;
+          //                                           return WikipediaFilter.TokenSTRIKETHROUGH;
+          //                                   }
+          break;
+        case 'h': // http(s)://
+          int urlStartPosition = fCurrentPosition;
+          boolean foundUrl = false;
+          int diff = 7;
+          try {
+            String urlString = fStringSource.substring(fCurrentPosition - 1, fCurrentPosition + 3);
+            if (urlString.equals("http")) {
+              fCurrentPosition += 3;
+              fCurrentCharacter = fSource[fCurrentPosition++];
+              if (fCurrentCharacter == 's') { // optional
+                fCurrentCharacter = fSource[fCurrentPosition++];
+                diff++;
+              }
+
+              if (fCurrentCharacter == ':' && fSource[fCurrentPosition++] == '/' && fSource[fCurrentPosition++] == '/') {
+                copyWhite(fWhiteStart, fWhiteStartPosition, diff);
+                fWhiteStart = false;
+                foundUrl = true;
+                while (WikipediaFilter.isUrlIdentifierPart(fSource[fCurrentPosition++])) {
+                }
+              }
+            }
+          } catch (IndexOutOfBoundsException e) {
+            if (!foundUrl) {
+              //              rollback work :-)
+              fCurrentPosition = urlStartPosition;
+            }
+          }
+          if (foundUrl) {
+            String urlString = new String(fSource, urlStartPosition - 1, fCurrentPosition - urlStartPosition);
+            fCurrentPosition--;
+            WikipediaFilter.createExternalLink(fResultBuffer, fWikiEngine, urlString);
+            continue;
+          }
+          break;
+
+        //        case '@': // images @xml@ -> /static/rss-small.png
+        //          copyWhite(fWhiteStart, fWhiteStartPosition, 1);
+        //          fWhiteStart = false;
+        //          int atStart = fCurrentPosition;
+        //          if (readUntilChar('@')) {
+        //            String imageTag = new String(fSource, atStart, fCurrentPosition - atStart - 1);
+        //            if (imageTag != null) {
+        //              if (WikipediaFilter.createStaticImage(imageTag, fResultBuffer)) {
+        //                continue;
+        //              }
+        //            }
+        //          }
+        //          fCurrentPosition = atStart;
+        //          break;
+        case '&':
+          int ampersandStart = fCurrentPosition - 1;
+          if (getNextChar('#')) {
+            try {
+              StringBuffer num = new StringBuffer(5);
+              char ch = fSource[fCurrentPosition++];
+              while (Character.isDigit(ch)) {
+                num.append(ch);
+                ch = fSource[fCurrentPosition++];
+              }
+              if (num.length() > 0 && ch == ';') {
+                Integer i = Integer.valueOf(num.toString());
+                if (i.intValue() < 65536) {
+                  copyWhite(fWhiteStart, fWhiteStartPosition, 3 + num.length());
+                  fWhiteStart = false;
+                  fResultBuffer.append(fSource, ampersandStart, fCurrentPosition - ampersandStart);
+                  continue;
+                }
+              }
+            } catch (IndexOutOfBoundsException e) {
+              // ignore exception
+            } catch (NumberFormatException e) {
+              // ignore exception
+            }
+          } else {
+            try {
+              StringBuffer entity = new StringBuffer(10);
+              char ch = fSource[fCurrentPosition++];
+              while (Character.isLetterOrDigit(ch)) {
+                entity.append(ch);
+                ch = fSource[fCurrentPosition++];
+              }
+              if (entity.length() > 0 && ch == ';') {
+                if (WikipediaFilter.ENTITY_SET.contains(entity.toString())) {
+                  copyWhite(fWhiteStart, fWhiteStartPosition, 2 + entity.length());
+                  fWhiteStart = false;
+                  fResultBuffer.append(fSource, ampersandStart, fCurrentPosition - ampersandStart);
+                  continue;
+                }
+              }
+            } catch (IndexOutOfBoundsException e) {
+              // ignore exception
+            } catch (NumberFormatException e) {
+              // ignore exception
+            }
+          }
+          break;
+        case '<':
+          int htmlStartPosition = fCurrentPosition;
+          try {
+            switch (fStringSource.charAt(fCurrentPosition)) {
+            case '!': // <!-- html comment -->
+              String htmlCommentString = fStringSource.substring(fCurrentPosition - 1, fCurrentPosition + 3);
+
+              if (htmlCommentString.equals("<!--")) {
+                fCurrentPosition += 3;
+                if (readUntilString("-->")) {
+                  String htmlCommentContent = new String(fSource, htmlStartPosition + 3, fCurrentPosition - htmlStartPosition - 6);
+                  if (htmlCommentContent != null) {
+                    copyWhite(fWhiteStart, fWhiteStartPosition, fCurrentPosition - htmlStartPosition + 1);
+                    fWhiteStart = false;
+                    // insert html comment for visual checks
+                    // only:
+                    /*
+                     * fResultBuffer.append(" <!--"); copyWhite(htmlCommentContent); fResultBuffer.append("--> ");
+                     */
+                    continue;
+                  }
+                }
+              }
+              break;
+            //                case 'm' : // math
+            //                  String mathString =
+            // fStringSource.substring(fCurrentPosition - 1,
+            // fCurrentPosition + 5);
+
+            //                  if (mathString.equals("<math>")) {
+            //                    fCurrentPosition += 5;
+            //                    if (readUntilString("</math>")) {
+            //                      String mathContent = new String(fSource,
+            // htmlStartPosition + 5, fCurrentPosition -
+            // htmlStartPosition - 12);
+            //                      if (mathContent != null) {
+            //                        copyWhite(fWhiteStart, fWhiteStartPosition,
+            // fCurrentPosition - htmlStartPosition + 1);
+            //                        fWhiteStart = false;
+            //                        if (startOfIndent) {
+            //                          startOfIndent = false;
+            //                          handleTeXMath(mathContent, false);
+            //                        } else {
+            //                          handleTeXMath(mathContent, true);
+            //                        }
+            //                        continue;
+            //                      }
+            //                    }
+            //                  }
+            //                  break;
+            case 'n': // nowiki
+              String nowikiString = fStringSource.substring(fCurrentPosition - 1, fCurrentPosition + 7);
+
+              if (nowikiString.equals("<nowiki>")) {
+                fCurrentPosition += 7;
+                if (readUntilString("</nowiki>")) {
+                  String nowikiContent = new String(fSource, htmlStartPosition + 7, fCurrentPosition - htmlStartPosition - 16);
+                  if (nowikiContent != null) {
+                    copyWhite(fWhiteStart, fWhiteStartPosition, fCurrentPosition - htmlStartPosition + 1);
+                    fWhiteStart = false;
+                    copyNowikiNewLine(nowikiContent);
+                    continue;
+                  }
+                }
+              }
+              break;
+            }
+          } catch (IndexOutOfBoundsException e) {
+
+          }
+          startOfIndent = false;
+          fCurrentPosition = htmlStartPosition;
+          // detect special html tags
+          int htmlToken = getHTMLToken();
+          if (htmlToken == WikipediaFilter.TokenIgnore) {
+            continue;
+            //              } else if (htmlToken > TokenIgnore) {
+            //                return htmlToken;
+          }
+          fCurrentPosition = htmlStartPosition;
+          break;
+        case '=': // wikipedia header ?
+          if (isStartOfLine()) {
+            int levelHeader = getNumberOfChar('=') + 1;
+            //            int tempPosition = checkWhitespaces(fWhiteStartPosition, fCurrentPosition - 1 - levelHeader);
+            //            if (tempPosition >= 0) {
+            copyWhite(fWhiteStart, fWhiteStartPosition, levelHeader);
+            fWhiteStart = false;
+            int startHeadPosition = fCurrentPosition;
+            //                 int initialOffset = levelHeader;
+            if (levelHeader > 6) {
+              levelHeader = 6;
+            }
+            levelHeader--;
+            if (readUntilString(WikipediaFilter.HEADER_STRINGS[levelHeader])) {
+              String head = new String(fSource, startHeadPosition, fCurrentPosition - startHeadPosition - (1 + levelHeader));
+              levelHeader++;
+              handleHead(head, levelHeader);
+              continue;
+            }
+            //            }
+          }
+          break;
+        }
+        if (!fWhiteStart) {
+          fWhiteStart = true;
+          fWhiteStartPosition = fCurrentPosition - 1;
+        }
+
+        startOfIndent = false;
+      }
+      //    -----------------end switch while try--------------------
+    } catch (IndexOutOfBoundsException e) {
+      // end of scanner text
+    }
+    copyWhite(fWhiteStart, fWhiteStartPosition, 1);
+
+    return WikipediaFilter.TokenEOF;
+  }
+
+  /**
+   * @return
+   */
+  private boolean isStartOfLine() {
+    boolean isListStart = false;
+    if (fCurrentPosition >= 2) {
+      char beforeChar = fSource[fCurrentPosition - 2];
+      if (beforeChar == '\n' || beforeChar == '\r') {
+        isListStart = true;
+      }
+    }
+    if (fCurrentPosition == 1) {
+      isListStart = true;
+    }
+    return isListStart;
+  }
+
+  /**
+   * @param levelStar
+   * @param listChars
+   *          TODO
+   */
+  private void appendList(char[] listChars) {
+    int topLevel = 0;
+    int levelStar = listChars.length;
+    copyWhite(fWhiteStart, fWhiteStartPosition, levelStar);
+    fWhiteStart = false;
+    AbstractTag tok = (AbstractTag) fTokenStack.peek();
+
+    if (tok instanceof ListToken) {
+      ListToken listToken = (ListToken) tok;
+      topLevel = listToken.getLevel();
+
+      if (levelStar > topLevel) {
+        while (levelStar > topLevel) {
+          if (listChars[topLevel] == '*') {
+            fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_UL_START, ++topLevel));
+            fResultBuffer.append("<ul><li>");
+          } else {
+            fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_OL_START, ++topLevel));
+            fResultBuffer.append("<ol><li>");
+          }
+        }
+      } else if (levelStar < topLevel) {
+        while (levelStar < topLevel) {
+          tok = (AbstractTag) fTokenStack.peek();
+          if (tok instanceof ListToken) {
+            fTokenStack.pop();
+            listToken = (ListToken) tok;
+            if (listToken.getToken() == WikipediaFilter.TokenLIST_UL_START) {
+              fResultBuffer.append("</li></ul></li><li>");
+            } else {
+              fResultBuffer.append("</li></ol></li><li>");
+            }
+            topLevel--;
+          } else {
+            break;
+          }
+        }
+      } else {
+        --topLevel;
+        if (listToken.getToken() == WikipediaFilter.TokenLIST_UL_START && listChars[topLevel] == '#') {
+          fTokenStack.pop();
+          fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_OL_START, topLevel));
+          fResultBuffer.append("</li></ul><ol><li>");
+        } else if (listToken.getToken() == WikipediaFilter.TokenLIST_OL_START && listChars[topLevel] == '*') {
+          fTokenStack.pop();
+          fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_UL_START, topLevel));
+          fResultBuffer.append("</li></ol><ul><li>");
+        } else {
+          fResultBuffer.append("</li><li>");
+        }
+      }
+    } else {
+      while (levelStar > topLevel) {
+        if (listChars[topLevel] == '*') {
+          fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_UL_START, ++topLevel));
+          fResultBuffer.append("\n<ul><li>");
+        } else {
+          fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_OL_START, ++topLevel));
+          fResultBuffer.append("\n<ol><li>");
+        }
+      }
+    }
+  }
+
+  private void createMacro(int startMacroPosition, String macroStartTag) {
+    String command = "";
+    String endTag;
+
+    String parameterString = null;
+    String macroBodyString = "";
+    int index0;
+    int index1;
+    if ((index0 = macroStartTag.indexOf(':')) >= 0) {
+      command = macroStartTag.substring(0, index0);
+      parameterString = macroStartTag.substring(index0 + 1, macroStartTag.length());
+    } else {
+      command = macroStartTag;
+    }
+    Macro macro = (Macro) fMacros.get(command);
+
+    String completeMacroSubString;
+
+    if ((macro != null) && (macro instanceof IBodyTagSupportMacro)) {
+      endTag = '{' + command + '}';
+      index0 = fStringSource.indexOf(endTag, fCurrentPosition);
+
+      if (index0 >= 0) {
+        macroBodyString = fStringSource.substring(fCurrentPosition, index0);
+        completeMacroSubString = new String(fSource, startMacroPosition - 1, index0 + endTag.length() - startMacroPosition + 1);
+        fCurrentPosition = startMacroPosition + completeMacroSubString.length() - 1;
+      } else {
+        completeMacroSubString = new String(fSource, startMacroPosition - 1, macroStartTag.length() + 2);
+      }
+    } else {
+      completeMacroSubString = new String(fSource, startMacroPosition - 1, macroStartTag.length() + 2);
+    }
+
+    copyWhite(fWhiteStart, fWhiteStartPosition, 1);
+
+    handleMacro(completeMacroSubString, command, parameterString, macroBodyString);
+  }
+
+  //  private void createExternalLink(String urlString) {
+  //
+  //    // Does our engine know images?
+  //    if (fWikiEngine instanceof ImageRenderEngine) {
+  //      fResult.append(((ImageRenderEngine) fWikiEngine).getExternalImageLink());
+  //    }
+  //    fResult.append("<span class=\"nobr\">");
+  //    fResult.append("<a href=\"");
+  //    fResult.append(Encoder.escape(urlString));
+  //    fResult.append("\">");
+  //    fResult.append(Encoder.toEntity(urlString.charAt(0)) +
+  // urlString.substring(1));
+  //    fResult.append("</a></span>");
+  //  }
+
+  //  private void handleTeXMath(String mathContent, boolean inlineExpression)
+  // {
+  //    // TODO clean this up
+  //    Map map = Application.get().getParameters();
+  //    HttpServletRequest request = (HttpServletRequest) map.get("request");
+  //    MathSniffer sniffy = MathSniffer.getBrowserDetection(request);
+  //
+  //    if (fCacheStaticBlockActive) {
+  //      fCachedPage.addHTML(fResultBuffer.substring(fCacheStaticBlockStartPosition,
+  // fResultBuffer.length()));
+  //      fCacheStaticBlockActive = false;
+  //    }
+  //    // if (inlineExpression) {
+  //    // fASMCompiler.compileMath(mathContent, "true");
+  //    // } else {
+  //    // fASMCompiler.compileMath(mathContent, "false");
+  //    // }
+  //    if (fCachedPage == WikipediaFilter.DUMMY_CACHED_PAGE) {
+  //      switch (sniffy.getBrowserId()) {
+  //        case MathSniffer.XMLID :
+  //        case MathSniffer.MATHPLAYERID :
+  //          if (inlineExpression) {
+  //            fResultBuffer.append("<m:math><m:mstyle mathcolor=\"Black\"
+  // displaystyle=\"true\">");
+  //            fTeXParser.initialize(mathContent);
+  //            fTeXParser.parse2MML(fResultBuffer);
+  //            fResultBuffer.append("</m:mstyle></m:math>");
+  //          } else {
+  //            fResultBuffer.append("<dl><dd><m:math><m:mstyle mathcolor=\"Black\"
+  // displaystyle=\"true\">");
+  //            fTeXParser.initialize(mathContent);
+  //            fTeXParser.parse2MML(fResultBuffer);
+  //            fResultBuffer.append("</m:mstyle></m:math></dd></dl>");
+  //          }
+  //          break;
+  //        case MathSniffer.CSSID :
+  //          if (inlineExpression) {
+  //            fResultBuffer.append("<m>");
+  //            fTeXParser.initialize(mathContent);
+  //            fTeXParser.parse2CSS(fResultBuffer, true);
+  //            fResultBuffer.append("</m>");
+  //          } else {
+  //            fResultBuffer.append("<e>");
+  //            fTeXParser.initialize(mathContent);
+  //            fTeXParser.parse2CSS(fResultBuffer, true);
+  //            fResultBuffer.append("</e>");
+  //          }
+  //          break;
+  //        default :
+  //          copyWhite(mathContent);
+  //      }
+  //    } else {
+  //      String[] mathStrings = new String[3];
+  //      StringBuffer mathBuffer = new StringBuffer();
+  //      if (inlineExpression) {
+  //        // mathml inline
+  //        mathBuffer.append("<m:math><m:mstyle mathcolor=\"Black\"
+  // displaystyle=\"true\">");
+  //
+  //        fTeXParser.initialize(mathContent);
+  //        fTeXParser.parse2MML(mathBuffer);
+  //        mathBuffer.append("</m:mstyle></m:math>");
+  //      } else {
+  //        mathBuffer.append("<dl><dd><m:math><m:mstyle mathcolor=\"Black\"
+  // displaystyle=\"true\">");
+  //        fTeXParser.initialize(mathContent);
+  //        fTeXParser.parse2MML(mathBuffer);
+  //        mathBuffer.append("</m:mstyle></m:math></dd></dl>");
+  //      }
+  //      mathStrings[0] = mathBuffer.toString();
+  //      mathBuffer.setLength(0);
+  //      // if (inlineExpression) {
+  //      // // css inline
+  //      // mathBuffer.append("<m>");
+  //      // fTeXParser.initialize(mathContent);
+  //      // fTeXParser.parse2CSS(mathBuffer, true);
+  //      // mathBuffer.append("</m>");
+  //      // } else {
+  //      // // css block mode
+  //      // mathBuffer.append("<e>");
+  //      // fTeXParser.initialize(mathContent);
+  //      // fTeXParser.parse2CSS(mathBuffer, true);
+  //      // mathBuffer.append("</e>");
+  //      // }
+  //
+  //      String encodedMathContent = Encoder.escape(mathContent);
+  //      StringBuffer mathmlBuffer = new StringBuffer();
+  //      String shortImageName = "____tex_" + (fImageCounter++);
+  //      String longImageName = shortImageName + ".gif";
+  //      mathmlBuffer.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
+  //      if (inlineExpression) {
+  //        mathmlBuffer.append("<math><mstyle mathcolor=\"Black\"
+  // displaystyle=\"true\">");
+  //        fTeXImageParser.initialize(mathContent);
+  //        fTeXImageParser.parse2MML(mathmlBuffer);
+  //        mathmlBuffer.append("</mstyle></math>");
+  //      } else {
+  //        mathmlBuffer.append("<math><mstyle mathcolor=\"Black\"
+  // displaystyle=\"true\">");
+  //        fTeXImageParser.initialize(mathContent);
+  //        fTeXImageParser.parse2MML(mathmlBuffer);
+  //        mathmlBuffer.append("</mstyle></math>");
+  //      }
+  //
+  //      String snipname = fSnip.getName();
+  //      String SnipSnapSpacePath =
+  // Application.get().getConfiguration().getFilePath().getAbsolutePath();
+  //      // Remove old image
+  //      fSnip.getAttachments().removeAttachment(longImageName + ".gif");
+  //
+  //      String directoryName = SnipSnapSpacePath + "/" + snipname;
+  //      File directory = new File(directoryName);
+  //      if (!directory.exists()) {
+  //        directory.mkdirs();
+  //      }
+  //      String gifFilename = directoryName + "/" + longImageName;
+  //      // File file = new File();
+  //      // Get the number of bytes in the file
+  //      // long length = file.length();
+  //      MathMLToGIFConverter conv = new MathMLToGIFConverter();
+  //      // String test =
+  //      // "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><math
+  //      //
+  // mode=\"display\"><mrow><munderover><mo>&#x0222B;</mo><mn>1</mn><mi>x</mi></munderover><mfrac><mi>dt</mi><mi>t</mi></mfrac></mrow></math>";
+  //      try {
+  //        File imageFile = conv.convert(mathmlBuffer.toString(), gifFilename);
+  //        if (imageFile != null) {
+  //          Attachment attachment =
+  //            new Attachment(longImageName, "image/gif", imageFile.length(), new
+  // Date(), snipname + "/" + longImageName, true);
+  //          // fSnip.getAttachments().addAttachment(longImageName, "image/gif",
+  // imageFile.length(), snipname + "/" + longImageName,
+  //          // true);
+  //          fSnip.getAttachments().addAttachment(attachment);
+  //          StringWriter writer = new StringWriter();
+  //
+  //          SnipLink.appendImage(writer, fSnip, shortImageName, encodedMathContent,
+  // "gif", null);
+  //
+  //          mathBuffer = writer.getBuffer();
+  //          mathStrings[1] = mathBuffer.toString();
+  //        } else {
+  //          mathStrings[1] = encodedMathContent;
+  //        }
+  //      } catch (IOException e) {
+  //        // TODO Auto-generated catch block
+  //        e.printStackTrace();
+  //        mathStrings[1] = encodedMathContent;
+  //      }
+  //      mathBuffer.setLength(0);
+  //      WikipediaFilter.copyWhite(mathBuffer, mathContent);
+  //      mathStrings[2] = mathBuffer.toString();
+  //      fCachedPage.addMath(mathStrings);
+  //    }
+  //    if (!fCacheStaticBlockActive) {
+  //      fCacheStaticBlockActive = true;
+  //      fCacheStaticBlockStartPosition = fResultBuffer.length();
+  //    }
+  //  }
+  private void handleSnipLink(String name) {
+    if (name != null) {
+      int index = name.indexOf("http://");
+      // Configuration probably wrote [http://radeox.org] instead of
+      // http://radeox.org
+      if (index != -1) {
+        // WikipediaFilter.createExternalLink(fResultBuffer,
+        // fWikiEngine, name.substring(index));
+        String urlString = name.substring(index);
+        // Wikipedia like style:
+        int pipeIndex = urlString.indexOf(' ');
+        String alias = "";
+        if (pipeIndex != (-1)) {
+          alias = urlString.substring(pipeIndex + 1);
+          urlString = urlString.substring(0, pipeIndex);
+        } else {
+          alias = urlString;
+        }
+
+        if (fWikiEngine instanceof ImageRenderEngine) {
+          fResultBuffer.append(((ImageRenderEngine) fWikiEngine).getExternalImageLink());
+        }
+        fResultBuffer.append("<span class=\"nobr\">");
+        fResultBuffer.append("<a href=\"");
+        fResultBuffer.append(Encoder.escape(urlString));
+        fResultBuffer.append("\">");
+        fResultBuffer.append(Encoder.toEntity(alias.charAt(0)) + alias.substring(1));
+        fResultBuffer.append("</a></span>");
+      }
+      //      else {
+      //        // trim the name and unescape it
+      //        name = Encoder.unescape(name.trim());
+      //        // Is there an alias like [alias|link] ?
+      //        int pipeIndex = name.indexOf('|');
+      //        String alias = "";
+      //        if (-1 != pipeIndex) {
+      //          alias = name.substring(0, pipeIndex);
+      //          name = name.substring(pipeIndex + 1);
+      //        }
+      //
+      //        int hashIndex = name.lastIndexOf('#');
+      //
+      //        String hash = "";
+      //        if (-1 != hashIndex && hashIndex != name.length() - 1) {
+      //          hash = name.substring(hashIndex + 1);
+      //          name = name.substring(0, hashIndex);
+      //        }
+      //
+      //        int colonIndex = name.indexOf(':');
+      //        // typed link ?
+      //        if (-1 != colonIndex) {
+      //          // for now throw away the fType information
+      //          name = name.substring(colonIndex + 1);
+      //        }
+      //
+      //        int atIndex = name.lastIndexOf('@');
+      //        // InterWiki link ?
+      //        if (-1 != atIndex) {
+      //          String extSpace = name.substring(atIndex + 1);
+      //          // known extarnal space ?
+      //          InterWiki interWiki = InterWiki.getInstance();
+      //          if (interWiki.contains(extSpace)) {
+      //            name = name.substring(0, atIndex);
+      //            Writer writer = new StringBufferWriter(fResultBuffer);
+      //            try {
+      //              if (-1 != hashIndex) {
+      //                interWiki.expand(writer, extSpace, name, hash);
+      //              } else {
+      //                interWiki.expand(writer, extSpace, name, "");
+      //              }
+      //            } catch (IOException e) {
+      //              log.debug("InterWiki " + extSpace + " not found.");
+      //            }
+      //          } else {
+      //            fResultBuffer.append("&#91;<span class=\"error\">");
+      //            fResultBuffer.append(name);
+      //            fResultBuffer.append("?</span>&#93;");
+      //          }
+      //        } else {
+      //          // internal link
+      //          if (fWikiEngine != null && fWikiEngine instanceof WikiRenderEngine) {
+      //            if (fCacheStaticBlockActive) {
+      //              fCachedPage.addHTML(fResultBuffer.substring(fCacheStaticBlockStartPosition, fResultBuffer.length()));
+      //              fCacheStaticBlockActive = false;
+      //            }
+      //            fCachedPage.addSnipLink(name);
+      //            if (fCachedPage == WikipediaFilter.DUMMY_CACHED_PAGE) {
+      //              if (((WikiRenderEngine) fWikiEngine).exists(name)) {
+      //                String view = name;
+      //                if (-1 != pipeIndex) {
+      //                  view = alias;
+      //                }
+      //                // Do not add hash if an alias was given
+      //                if (-1 != hashIndex) {
+      //                  ((WikiRenderEngine) fWikiEngine).appendLink(fResultBuffer, name, view, hash);
+      //                } else {
+      //                  ((WikiRenderEngine) fWikiEngine).appendLink(fResultBuffer, name, view);
+      //                }
+      //              } else if (((WikiRenderEngine) fWikiEngine).showCreate()) {
+      //                ((WikiRenderEngine) fWikiEngine).appendCreateLink(fResultBuffer, name, name);
+      //                // links with "create" are not cacheable because
+      //                // a missing wiki could be created
+      //                fContext.getRenderContext().setCacheable(false);
+      //              } else {
+      //                // cannot display/create wiki, so just display
+      //                // the text
+      //                fResultBuffer.append(name);
+      //              }
+      //            }
+      //            if (!fCacheStaticBlockActive) {
+      //              fCacheStaticBlockActive = true;
+      //              fCacheStaticBlockStartPosition = fResultBuffer.length();
+      //            }
+      //          } else {
+      //            // cannot display/create wiki, so just display the text
+      //            fResultBuffer.append(name);
+      //          }
+      //        }
+      //      }
+    }
+  }
+
+  private boolean handleWikipediaTable() {
+    //  // example
+    //// {| border=1
+    //// |Zelle 1
+    //// |
+    //// {| border=2
+    //// |Zelle A
+    //// |-
+    //// |Zelle B
+    //// |}
+    //// |Zelle 3
+    //// |}
+    int temp = fCurrentPosition;
+    int sequenceStart = 0;
+    char lastChar = ' ';
+    int sequenceLength = 0;
+    int thStartPosition = 0;
+    Stack wpTokenStack = new Stack();
+    try {
+      // add table attributes:
+      fResultBuffer.append("<table ");
+      sequenceStart = fCurrentPosition;
+      fCurrentCharacter = fSource[fCurrentPosition++];
+      while (fCurrentCharacter != '\n' && fCurrentCharacter != '\r') {
+        sequenceLength++;
+        fCurrentCharacter = fSource[fCurrentPosition++];
+      }
+      if (fCurrentCharacter == '\n' || fCurrentCharacter == '\r') {
+        fCurrentCharacter = fSource[fCurrentPosition++];
+      }
+      if (sequenceLength > 0) {
+        fResultBuffer.append(fSource, sequenceStart + 1, sequenceLength - 1);
+      }
+      fResultBuffer.append(">");
+      wpTokenStack.push(WikipediaFilter.HTML_TABLE_OPEN);
+
+      fResultBuffer.append("<tr>\n");
+      wpTokenStack.push(WikipediaFilter.HTML_TR_OPEN);
+      String attributes = null;
+      // parse rest of table
+      while (true) {
+        sequenceStart = fCurrentPosition;
+        fCurrentCharacter = fSource[fCurrentPosition++];
+        if (fCurrentCharacter == '{' && fSource[fCurrentPosition] == '|') { // nested table
+          handleWikipediaTable();
+        } else if (fCurrentCharacter == '!') {
+          // <th>...</th>
+          reduceTableCellStack(fResultBuffer, wpTokenStack);
+          fResultBuffer.append(fSource, sequenceStart, fCurrentPosition - sequenceStart - 1);
+          sequenceStart = fCurrentPosition;
+          // add header row cells
+          attributes = null;
+          while (true) {
+            fCurrentCharacter = fSource[fCurrentPosition++];
+            if (fCurrentCharacter == '|' || fCurrentCharacter == '\n') {
+              if (fSource[fCurrentPosition] == '|' || fCurrentCharacter == '\n') {
+                reduceTableCellStack(fResultBuffer, wpTokenStack);
+                if (attributes == null) {
+                  fResultBuffer.append("<th>");
+                } else {
+                  fResultBuffer.append("<th ");
+                  fResultBuffer.append(attributes);
+                  fResultBuffer.append(">");
+                }
+                wpTokenStack.push(WikipediaFilter.HTML_TH_OPEN);
+                fResultBuffer.append(WikipediaFilter.filterParser(new String(fSource, sequenceStart, fCurrentPosition
+                    - sequenceStart - 1), fContext, fMacros, fRecursionLevel));
+                fCurrentPosition++;
+                sequenceStart = fCurrentPosition;
+                attributes = null;
+              } else {
+                attributes = new String(fSource, sequenceStart, fCurrentPosition - sequenceStart - 1);
+                sequenceStart = fCurrentPosition;
+              }
+            }
+            if (fCurrentCharacter == '\n') {
+              fCurrentPosition--;
+              break;
+            }
+          }
+        } else if (fCurrentCharacter == '|') {
+          reduceTableCellStack(fResultBuffer, wpTokenStack);
+          fResultBuffer.append(fSource, sequenceStart, fCurrentPosition - sequenceStart - 1);
+
+          sequenceStart = fCurrentPosition;
+          fCurrentCharacter = fSource[fCurrentPosition++];
+          switch (fCurrentCharacter) {
+          case '+': // caption
+            sequenceStart++;
+            reduceTableRowStack(WikipediaFilter.HTML_TABLE_OPEN, fResultBuffer, wpTokenStack);
+            fResultBuffer.append("<caption>\n");
+            wpTokenStack.push(WikipediaFilter.HTML_CAPTION_OPEN);
+            // read until end of line
+            while (fCurrentCharacter != '\n' && fCurrentCharacter != '\r') {
+              fCurrentCharacter = fSource[fCurrentPosition++];
+            }
+            if (fCurrentCharacter == '\n' || fCurrentCharacter == '\r') {
+              fCurrentCharacter = fSource[fCurrentPosition++];
+            }
+            fResultBuffer.append(WikipediaFilter.filterParser(new String(fSource, sequenceStart, fCurrentPosition - sequenceStart
+                - 1), fContext, fMacros, fRecursionLevel));
+            break;
+          case '-': // new row
+            sequenceStart++;
+            reduceTableRowStack(WikipediaFilter.HTML_TR_OPEN, fResultBuffer, wpTokenStack);
+
+            // read until end of line
+            while (fCurrentCharacter != '\n' && fCurrentCharacter != '\r') {
+              fCurrentCharacter = fSource[fCurrentPosition++];
+            }
+            if (fCurrentCharacter == '\n' || fCurrentCharacter == '\r') {
+              fCurrentCharacter = fSource[fCurrentPosition++];
+            }
+            // TODO handle row attributes
+            fResultBuffer.append("<tr ");
+            wpTokenStack.push(WikipediaFilter.HTML_TR_OPEN);
+            fResultBuffer.append(fSource, sequenceStart, fCurrentPosition - sequenceStart - 1);
+            fResultBuffer.append(">\n");
+            //            fResultBuffer.append(WikipediaFilter.filterParser(new String(fSource, sequenceStart, fCurrentPosition
+            //                - sequenceStart - 1), fContext, fMacros, fRecursionLevel));
+            break;
+          case '}': // end of table
+            clearTableStack(fResultBuffer, wpTokenStack);
+            //                                         System.out.println(fResultBuffer.toString());
+            return true;
+          default:
+            // add row cells
+            attributes = null;
+            while (true) {
+              fCurrentCharacter = fSource[fCurrentPosition++];
+              if (fCurrentCharacter == '|' || fCurrentCharacter == '\n') {
+                if (fSource[fCurrentPosition] == '|' || fCurrentCharacter == '\n') {
+                  reduceTableCellStack(fResultBuffer, wpTokenStack);
+                  if (attributes == null) {
+                    fResultBuffer.append("<td>");
+                  } else {
+                    fResultBuffer.append("<td ");
+                    fResultBuffer.append(attributes);
+                    fResultBuffer.append(">");
+                  }
+                  wpTokenStack.push(WikipediaFilter.HTML_TD_OPEN);
+                  fResultBuffer.append(WikipediaFilter.filterParser(new String(fSource, sequenceStart, fCurrentPosition
+                      - sequenceStart - 1), fContext, fMacros, fRecursionLevel));
+                  // TODO reduce fTokenStack to <tr> element
+                  // if necessary
+                  //                  fResultBuffer.append("</td>");
+                  //                  fTokenStack.pop();
+                  fCurrentPosition++;
+                  sequenceStart = fCurrentPosition;
+                  attributes = null;
+                } else {
+                  attributes = new String(fSource, sequenceStart, fCurrentPosition - sequenceStart - 1);
+
+                  sequenceStart = fCurrentPosition;
+                }
+              }
+              if (fCurrentCharacter == '\n') {
+                fCurrentPosition--;
+                break;
+              }
+            }
+          }
+        } else if (wpTokenStack.peek() == WikipediaFilter.HTML_TD_OPEN) {
+          // continue a table cell in the next line
+          while (true) {
+            if (fCurrentCharacter == '\n') {
+              char ch0 = fSource[fCurrentPosition];
+              char ch1 = fSource[fCurrentPosition + 1];
+              if (ch0 == '|' || ch0 == '!' || (ch0 == '{' && ch1 == '|')) {
+                fResultBuffer.append(WikipediaFilter.filterParser(new String(fSource, sequenceStart, fCurrentPosition
+                    - sequenceStart - 1), fContext, fMacros, fRecursionLevel));
+                sequenceStart = fCurrentPosition;
+                attributes = null;
+                break;
+              }
+            }
+            fCurrentCharacter = fSource[fCurrentPosition++];
+          }
+        }
+      }
+    } catch (IndexOutOfBoundsException e) {
+      if (sequenceStart + 1 < fCurrentPosition && fSource[sequenceStart + 1] == '}') {
+        //                              TODO reduce fTokenStack to <tr> element if necessary
+        //        fResultBuffer.append("</tr></table>\n");
+        //        wpTokenStack.pop(); // tr
+        //        wpTokenStack.pop();// table
+        //                             System.out.println(fResultBuffer.toString());
+        clearTableStack(fResultBuffer, wpTokenStack);
+        return true;
+      }
+    }
+    fCurrentPosition = temp - 1;
+    //         System.out.print(fResultBuffer.toString());
+    return false;
+  } //  private boolean handleWikipediaTable() {
+
+  private void clearTableStack(StringBuffer buffer, Stack stack) {
+    CloseTagToken closeTag;
+    AbstractTag tag;
+    while (!stack.isEmpty()) {
+      tag = (AbstractTag) stack.pop();
+      closeTag = (CloseTagToken) WikipediaFilter.CLOSE_TAGS.get(tag.getTagName());
+      buffer.append(closeTag.getCloseTag());
+    }
+  }
+
+  private void reduceTableRowStack(AbstractTag stopTag, StringBuffer buffer, Stack stack) {
+    CloseTagToken closeTag;
+    AbstractTag tag;
+    while (!stack.isEmpty()) {
+      tag = (AbstractTag) stack.peek();
+      if (tag == WikipediaFilter.HTML_TABLE_OPEN) {
+        break;
+      }
+      stack.pop();
+      closeTag = (CloseTagToken) WikipediaFilter.CLOSE_TAGS.get(tag.getTagName());
+      buffer.append(closeTag.getCloseTag());
+      if (tag == WikipediaFilter.HTML_TR_OPEN || tag == WikipediaFilter.HTML_CAPTION_OPEN) {
+        break;
+      }
+    }
+  }
+
+  private void reduceTableCellStack(StringBuffer buffer, Stack stack) {
+    CloseTagToken closeTag;
+    AbstractTag tag;
+    while (!stack.isEmpty()) {
+      tag = (AbstractTag) stack.peek();
+      if (tag != WikipediaFilter.HTML_TH_OPEN && tag != WikipediaFilter.HTML_TD_OPEN) {
+        break;
+      }
+      stack.pop();
+      closeTag = (CloseTagToken) WikipediaFilter.CLOSE_TAGS.get(tag.getTagName());
+      buffer.append(closeTag.getCloseTag());
+      if (tag == WikipediaFilter.HTML_TR_OPEN || tag == WikipediaFilter.HTML_CAPTION_OPEN || tag == WikipediaFilter.HTML_TH_OPEN
+          || tag == WikipediaFilter.HTML_TD_OPEN) {
+        break;
+      }
+    }
+  }
+
+  //    // example
+  //// {| border=1
+  //// |Zelle 1
+  //// |
+  //// {| border=2
+  //// |Zelle A
+  //// |-
+  //// |Zelle B
+  //// |}
+  //// |Zelle 3
+  //// |}
+  //
+  //    int temp = fCurrentPosition;
+  //    // StringBuffer suffixBuffer = new StringBuffer();
+  //    Table table = new Table(fContext.getRenderContext());
+  //    // char currentChar = ' ';
+  //    char lastChar = ' ';
+  //    // int startPosition = 0;
+  //    // int currentPosition = 0;
+  //    StringBuffer buffer = new StringBuffer();
+  //    int thStartPosition = 0;
+  //
+  //    try { // read first line
+  //      fCurrentCharacter = fSource[fCurrentPosition++];
+  //      // TODO improve this for different table attributes
+  //      while (fCurrentCharacter != '\n' ) {
+  //        fCurrentCharacter = fSource[fCurrentPosition++];
+  //        // System.out.println(fCurrentCharacter);
+  //      }
+  //      table.newRow();
+  //      lastChar = fCurrentCharacter;
+  //      fCurrentCharacter = fSource[fCurrentPosition++];
+  //      while (true) {
+  //
+  //        switch (fCurrentCharacter) {
+  //          // case '{' : // start of nested table ?
+  //          // if (getNextChar('|') && handleWikipediaTable()) { // Wikipedia table
+  // end reached
+  //          // return true;
+  //          // }
+  //          // break;
+  //          case '!' :
+  //            if (lastChar == '\n') {
+  //              table.addCell(buffer.toString());
+  //              // System.out.println(buffer.toString());
+  //              buffer.setLength(0);
+  //              thStartPosition = fCurrentPosition;
+  //              while (true) {
+  //                lastChar = fCurrentCharacter;
+  //                fCurrentCharacter = fSource[fCurrentPosition++];
+  //                if (fCurrentCharacter == '|') {
+  //                  break;
+  //                }
+  //                if (fCurrentCharacter == '\n') {
+  //                  lastChar = '\n';
+  //                  fCurrentPosition = thStartPosition;
+  //                  break;
+  //                }
+  //              }
+  //            } else {
+  //              buffer.append(fCurrentCharacter);
+  //            }
+  //            break;
+  //          case '|' :
+  //            if (lastChar == '\n') {
+  //              if (getNextChar('}')) { // Wikipedia table end reached
+  //                table.addCell(buffer.toString());
+  //                StringWriter writer = new StringWriter();
+  //                try {
+  //                  table.appendTo(writer);
+  //                  fResultBuffer.append(writer.getBuffer());
+  //                } catch (IOException e1) {
+  //                  // TODO Auto-generated catch block
+  //                  e1.printStackTrace();
+  //                  return false;
+  //                }
+  //                return true;
+  //              } else if (getNextChar('-')) {
+  //                table.addCell(buffer.toString());
+  //                buffer.setLength(0);
+  //                table.newRow();
+  //                while (true) {
+  //                  lastChar = fCurrentCharacter;
+  //                  fCurrentCharacter = fSource[fCurrentPosition++];
+  //                  if (fCurrentCharacter == '|' || fCurrentCharacter == '!') {
+  //                    break;
+  //                  }
+  //                }
+  //               // continue;
+  //              } else {
+  //                if (buffer.length()>0) {
+  //                  table.addCell(buffer.toString());
+  //                  buffer.setLength(0);
+  //                }
+  //              }
+  //            } else if (getNextChar('|')) {
+  //              table.addCell(buffer.toString());
+  //              // System.out.println(buffer.toString());
+  //              buffer.setLength(0);
+  //            } else {
+  //              buffer.append(fCurrentCharacter);
+  //            }
+  //            break;
+  //          default :
+  //            buffer.append(fCurrentCharacter);
+  //        }
+  //        lastChar = fCurrentCharacter;
+  //        fCurrentCharacter = fSource[fCurrentPosition++];
+  //      }
+  //
+  //    } catch (IndexOutOfBoundsException e) {
+  //
+  //    }
+  //    fCurrentPosition = temp - 1;
+  //    return false;
+  //  }
+  private void handleWikipediaLink(String linkText, String suffix) {
+    String name = linkText;
+    if (name != null) {
+      int index = name.indexOf("http://");
+      // Configuration probably wrote [http://radeox.org] instead of
+      // http://radeox.org
+      if (index != -1) {
+        WikipediaFilter.createExternalLink(fResultBuffer, fWikiEngine, name.substring(index));
+        // show error
+        // fResult.append("<div class=\"error\">Do not surround URLs
+        // with [...].</div>");
+      } else {
+        // trim the name and unescape it
+        name = Encoder.unescape(name.trim());
+        //                Is there an alias like [alias|link] ?
+        int pipeIndex = name.indexOf('|');
+        String alias = "";
+        if (-1 != pipeIndex) {
+          alias = name.substring(pipeIndex + 1);
+          name = name.substring(0, pipeIndex);
+        }
+
+        int hashIndex = name.lastIndexOf('#');
+
+        String hash = "";
+        if (-1 != hashIndex && hashIndex != name.length() - 1) {
+          hash = name.substring(hashIndex + 1);
+          name = name.substring(0, hashIndex);
+        }
+
+        //                             int colonIndex = name.indexOf(':');
+        //                             // typed link ?
+        //                             if (-1 != colonIndex) {
+        //                                     // for now throw away the fType information
+        //                                     name = name.substring(colonIndex + 1);
+        //                             }
+
+        int atIndex = name.lastIndexOf('@');
+        // InterWiki link ?
+        if (-1 != atIndex) {
+          String extSpace = name.substring(atIndex + 1);
+          // known extarnal space ?
+          InterWiki interWiki = InterWiki.getInstance();
+          if (interWiki.contains(extSpace)) {
+            name = name.substring(0, atIndex);
+            Writer writer = new StringBufferWriter(fResultBuffer);
+            try {
+              if (-1 != hashIndex) {
+                interWiki.expand(writer, extSpace, name, hash);
+              } else {
+                interWiki.expand(writer, extSpace, name, "");
+              }
+            } catch (IOException e) {
+              //              log.debug("InterWiki " + extSpace + " not found.");
+            }
+          } else {
+            fResultBuffer.append("&#91;<span class=\"error\">");
+            fResultBuffer.append(name);
+            fResultBuffer.append("?</span>&#93;");
+          }
+        } else {
+          // internal link
+          if (name.startsWith("Image:") && (fWikiEngine instanceof ImageRenderEngine)) {
+            // server part of rendering images
+            ImageRenderEngine imageEngine = (ImageRenderEngine) fWikiEngine;
+            //                 fResultBuffer.append("<img src=\"space/1/2004-11-21/5/");
+            fResultBuffer.append("<img src=\"");
+            fResultBuffer.append(FilterUtil.createServerImage(null, name.substring(6), null));
+            fResultBuffer.append("\">");
+          } else if (fWikiEngine != null && fWikiEngine instanceof WikiRenderEngine) {
+            if (((WikiRenderEngine) fWikiEngine).exists(name)) {
+              String view = name + suffix;
+              if (-1 != pipeIndex) {
+                view = alias + suffix;
+              }
+              if (name.startsWith("Image:")) {
+                fResultBuffer.append("<img src=\"");
+
+                fResultBuffer.append(FilterUtil.createHTMLLink(null, name, null));
+                fResultBuffer.append("\">");
+              } else {
+                // Do not add hash if an alias was given
+                if (-1 != hashIndex) {
+                  ((WikiRenderEngine) fWikiEngine).appendLink(fResultBuffer, name, view, hash);
+                } else {
+                  ((WikiRenderEngine) fWikiEngine).appendLink(fResultBuffer, name, view);
+                }
+              }
+            } else if (((WikiRenderEngine) fWikiEngine).showCreate()) {
+              ((WikiRenderEngine) fWikiEngine).appendCreateLink(fResultBuffer, name, name + suffix);
+              // links with "create" are not cacheable because
+              // a missing wiki could be created
+              fContext.getRenderContext().setCacheable(false);
+            } else {
+              // cannot display/create wiki, so just display
+              // the text
+              fResultBuffer.append(name);
+            }
+          } else {
+            // cannot display/create wiki, so just display the text
+            fResultBuffer.append(name);
+          }
+        }
+      }
+    }
+  }
+
+  public String createAnchor(String head) {
+    StringBuffer result = new StringBuffer(head.length() + 1);
+    char ch;
+    result.append('a');
+    // reduce Anchorstring
+    for (int i = 0; i < head.length(); i++) {
+      ch = head.charAt(i);
+      if ('a' <= ch && 'z' >= ch) {
+        result.append(ch);
+      } else if ('A' <= ch && 'Z' >= ch) {
+        result.append(ch);
+      } else if ('0' <= ch && '9' >= ch) {
+        result.append(ch);
+      }
+      //        switch (ch) {
+      //          case ' ' :
+      //            result.append('_');
+      //            break;
+      //          case '<' : // special html escape character
+      //            fResult.append(Encoder.toEntity('<'));
+      //            break;
+      //          case '>' : // special html escape character
+      //            fResult.append(Encoder.toEntity('>'));
+      //            break;
+      //          case '&' : // special html escape character
+      //            fResult.append(Encoder.toEntity('&'));
+      //            break;
+      //          case '\'' : // special html escape character
+      //            fResult.append(Encoder.toEntity('\''));
+      //            break;
+      //          case '\"' : // special html escape character
+      //            fResult.append(Encoder.toEntity('\"'));
+      //            break;
+      //          default :
+      //            result.append(ch);
+      //        }
+    }
+    return result.toString();
+  }
+
+  public static StringBuffer appendLink(StringBuffer buffer, String name, String view, String target) {
+    return appendLinkWithRoot(buffer, null, name + "#" + target, view);
+  }
+
+  /**
+   * Create a link with a root and a special view. The name will not be url encoded!
+   */
+  public static StringBuffer appendLinkWithRoot(StringBuffer buffer, String root, String name, String view) {
+    buffer.append("<a href=\"");
+    if (root != null) {
+      buffer.append(root);
+      buffer.append("/");
+    }
+    buffer.append(name);
+    buffer.append("\">");
+    buffer.append(Encoder.escape(view));
+    buffer.append("</a>");
+    return buffer;
+  }
+
+  /**
+   * add an entry to the "table of content" TODO refactor this to a class
+   * 
+   * @param toc
+   * @param head
+   * @param anchor
+   * @param headLevel
+   */
+  private void addToTableOfContent(ArrayList toc, String head, String anchor, int headLevel) {
+    int level = 1;
+    if (level == headLevel) {
+      String snipName = "";
+      //      if (fSnip != null) {
+      //        snipName = fSnip.getName();
+      //      }
+
+      StringBuffer link = new StringBuffer(snipName.length() + 40 + head.length() + anchor.length());
+      link.append("<li>");
+      //TODO create link for table of content
+      appendLink(link, snipName, head, anchor);
+      link.append("</li>");
+      toc.add(link.toString());
+    } else {
+      if (toc.size() > 0) {
+        if (toc.get(toc.size() - 1) instanceof ArrayList) {
+          addToTableOfContent((ArrayList) toc.get(toc.size() - 1), head, anchor, --headLevel);
+          return;
+        }
+      }
+      ArrayList list = new ArrayList();
+      toc.add(list);
+      addToTableOfContent(list, head, anchor, --headLevel);
+    }
+  }
+
+  /**
+   * handle head for table of content
+   * 
+   * @param head
+   * @param headLevel
+   */
+  private void handleHead(String head, int headLevel) {
+    if (head != null) {
+      String anchor = createAnchor(head.trim());
+
+      if (fTableOfContent == null) {
+        // create new table of content
+        fTableOfContent = new ArrayList();
+        // copy fResult and new initialization:
+
+        fResultBufferHeader = fResultBuffer;
+        fResultBuffer = new StringBuffer(fResultBuffer.capacity());
+      }
+      addToTableOfContent(fTableOfContent, head, anchor, headLevel);
+
+      fResultBuffer.append("<h");
+      fResultBuffer.append(headLevel);
+      fResultBuffer.append("><a name=\"");
+      fResultBuffer.append(anchor);
+      fResultBuffer.append("\">");
+      fResultBuffer.append(head);
+      fResultBuffer.append("</a></h");
+      fResultBuffer.append(headLevel);
+      fResultBuffer.append(">");
+      //      if (headLevel <= 2) {
+      //        fResultBuffer.append("<hr/>");
+      //      }
+    }
+  }
+
+  private boolean getList(char listChar, String openTag, String closeTag) {
+    int currentPosition = fCurrentPosition;
+    int level = getNumberOfChar(listChar) + 1;
+    if (getNextChar('.') && getNextChar(' ')) {
+      int tempPosition = checkWhitespaces(fWhiteStartPosition, fCurrentPosition - 3 - level);
+      if (tempPosition >= 0) {
+        copyWhite(fWhiteStart, fWhiteStartPosition, 2 + level);
+        fWhiteStart = false;
+        AbstractTag tok = (AbstractTag) fTokenStack.peek();
+        if (tok instanceof ListToken) {
+          ListToken listToken = (ListToken) tok;
+          int topLevel = listToken.getLevel();
+          if (listToken.getToken() == WikipediaFilter.TokenLIST_OL_START) {
+            if (level > topLevel) {
+              fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_OL_START, topLevel + 1));
+              fResultBuffer.append(openTag + "<li>");
+            } else if (level < topLevel) {
+              fTokenStack.pop();
+              fResultBuffer.append("</li>" + closeTag + "</li><li>");
+            } else {
+              fResultBuffer.append("</li><li>");
+            }
+          } else {
+            fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_OL_START, level));
+            fResultBuffer.append(openTag + "<li>");
+          }
+        } else {
+          fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_OL_START, 1));
+          fResultBuffer.append("\n" + openTag + "<li>");
+        }
+        return true;
+      }
+    }
+    fCurrentPosition = currentPosition;
+    return false;
+  }
+
+  /**
+   * read until the string is found
+   * 
+   * @param name
+   * @return
+   */
+  private final boolean readUntilString(String testedString) {
+    int temp = fCurrentPosition;
+    int index = fStringSource.indexOf(testedString, fCurrentPosition);
+    if (index != (-1)) {
+      fCurrentPosition = index + testedString.length();
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * read until character is found
+   * 
+   * @param name
+   * @return
+   */
+  private 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 end-of-line is reached
+   * 
+   * @param name
+   * @return -1 - for IndexOutOfBoundsException; 0 - for LF found; 1 - for testedChar found
+   */
+  private final int readUntilCharOrEOL(char testedChar) {
+    int temp = fCurrentPosition;
+    try {
+      while ((fCurrentCharacter = fSource[fCurrentPosition++]) != testedChar) {
+        //                             if (fCurrentCharacter == '\n') {
+        //                                     return 0;
+        //                             }
+      }
+      return 1;
+    } catch (IndexOutOfBoundsException e) {
+      fCurrentPosition = temp;
+      return -1;
+    }
+  }
+
+  /**
+   * read until character is found or end-of-line is reached
+   * 
+   * @param name
+   * @return -1 - for IndexOutOfBoundsException; 0 - for LF found; 1 - for testedChar found
+   */
+  private final boolean readUntilEOL() {
+    int temp = fCurrentPosition;
+    try {
+      while (true) {
+        fCurrentCharacter = fSource[fCurrentPosition++];
+        if (fCurrentCharacter == '\n' || fCurrentCharacter == '\r') {
+          return true;
+        }
+      }
+    } catch (IndexOutOfBoundsException e) {
+      --fCurrentPosition;
+      return true;
+    }
+  }
+
+  /**
+   * Returns the view of the wiki name that is shown to the fUser. Overwrite to support other views for example transform
+   * "WikiLinking" to "Wiki Linking". Does nothing by default.
+   * 
+   * @return view The view of the wiki name
+   */
+  //  protected String getWikiView(String name) {
+  //    return name;
+  //  }
+  private void handleMacro(String completeMacroSubString, String command, String unsplittedMacroParameters, String group3) {
+    if (command != null) {
+      // {$peng} are variables not macros.
+      if (!command.startsWith("$")) {
+        MacroParameter mParams = fContext.getMacroParameter();
+
+        if (group3 != null) {
+          mParams.setContent(group3);
+          mParams.setContentStart(0);
+          mParams.setContentEnd(group3.length());
+        }
+        if (unsplittedMacroParameters != null && unsplittedMacroParameters.length() > 1) {
+          //            mParams.setParams(parseParameters(unsplittedMacroParameters));
+          mParams.setParams(unsplittedMacroParameters);
+        }
+        mParams.setStart(0);
+        mParams.setEnd(completeMacroSubString.length());
+
+        // @DANGER: recursive calls may replace macros in included
+        // source code
+        try {
+          if (fMacros.containsKey(command)) {
+            Macro macro = (Macro) fMacros.get(command);
+
+            // recursively filter macros within macros
+            if (null != mParams.getContent() && !(macro instanceof INoParserBodyFilterMacro)) {
+              mParams.setContent(WikipediaFilter.filterParser(mParams.getContent(), fContext, fMacros, fRecursionLevel));
+            }
+            StringBufferWriter writer = new StringBufferWriter(new StringBuffer(256));
+            macro.execute(writer, mParams);
+            StringBuffer buffer = writer.getBuffer();
+            if (macro instanceof IRenderResultMacro) {
+              fResultBuffer.append(WikipediaFilter.filterParser(buffer.toString(), fContext, fMacros, fRecursionLevel));
+            } else {
+              fResultBuffer.append(buffer);
+            }
+
+          } else if (command.startsWith("!")) {
+
+            RenderEngine engine = fContext.getRenderContext().getRenderEngine();
+            if (engine instanceof IncludeRenderEngine) {
+              String include = ((IncludeRenderEngine) engine).include(command.substring(1));
+              if (null != include) {
+                // Filter paramFilter = new
+                // ParamFilter(mParams);
+                // included = paramFilter.filter(included,
+                // null);
+                //                  fResult.append(include);
+
+                fResultBuffer.append(WikipediaFilter.filterParser(include, fContext, fMacros, fRecursionLevel));
+
+              } else {
+                fResultBuffer.append(command.substring(1) + " not found.");
+              }
+            }
+
+            return;
+          } else {
+            //              fResult.append(group0);
+            copyWhite(completeMacroSubString);
+            return;
+          }
+        } catch (IllegalArgumentException e) {
+
+          fResultBuffer.append("<div class=\"error\">" + command + ": " + e.getMessage() + "</div>");
+
+          e.printStackTrace();
+
+        } catch (Throwable e) {
+          //          log.warn("MacroFilter: unable to format macro: " + command, e);
+          fResultBuffer.append("<div class=\"error\">" + command + ": " + e.getMessage() + "</div>");
+          e.printStackTrace();
+          return;
+        }
+      } else {
+        fResultBuffer.append("<");
+        fResultBuffer.append(command.substring(1));
+        fResultBuffer.append(">");
+      }
+    } else {
+      //        fResult.append(group0);
+      copyWhite(completeMacroSubString);
+    }
+  }
+
+  public void parse() {
+    int token = WikipediaFilter.TokenSTART;
+    fTokenStack.add(WikipediaFilter.START);
+    //    fListStack.add(START);
+    try {
+      while ((token = getNextToken()) != WikipediaFilter.TokenEOF) {
+        switch (token) {
+        case WikipediaFilter.TokenBOLD:
+          if (fTokenStack.peek() == WikipediaFilter.BOLD) {
+            fTokenStack.pop();
+            fResultBuffer.append("</b>");
+          } else {
+            fTokenStack.push(WikipediaFilter.BOLD);
+            fResultBuffer.append("<b>");
+          }
+          break;
+        case WikipediaFilter.TokenITALIC:
+          if (fTokenStack.peek() == WikipediaFilter.ITALIC) {
+            fTokenStack.pop();
+            fResultBuffer.append("</i>");
+          } else {
+            fTokenStack.push(WikipediaFilter.ITALIC);
+            fResultBuffer.append("<i>");
+          }
+          break;
+        case WikipediaFilter.TokenSTRONG:
+          if (fTokenStack.peek() == WikipediaFilter.STRONG) {
+            fTokenStack.pop();
+            fResultBuffer.append("</strong>");
+          } else {
+            fTokenStack.push(WikipediaFilter.STRONG);
+            fResultBuffer.append("<strong>");
+          }
+          break;
+        case WikipediaFilter.TokenEM:
+          if (fTokenStack.peek() == WikipediaFilter.EM) {
+            fTokenStack.pop();
+            fResultBuffer.append("</em>");
+          } else {
+            fTokenStack.push(WikipediaFilter.EM);
+            fResultBuffer.append("<em>");
+          }
+          break;
+        case WikipediaFilter.TokenSTRIKETHROUGH:
+          if (fTokenStack.peek() == WikipediaFilter.STRIKETHROUGH) {
+            fTokenStack.pop();
+            fResultBuffer.append("</strike>");
+          } else {
+            fTokenStack.push(WikipediaFilter.STRIKETHROUGH);
+            fResultBuffer.append("<strike>");
+          }
+          break;
+        //            case TokenLIST_UL_START :
+        //              if (fTokenStack.peek().equals(LIST_UL_START)) {
+        //                fResult.append("</li>\n<li>");
+        //              } else {
+        //                fTokenStack.push(LIST_UL_START);
+        //                fResult.append("\n<ul class=\"star\">\n<li>");
+        //              }
+        //              break;
+        //            case TokenLIST_UL_END :
+        //              fTokenStack.pop();
+        //              fResult.append("</li>\n</ul>\n");
+        //              break;
+        //            case TokenLIST_OL_START :
+        //              if (fTokenStack.peek().equals(LIST_OL_START)) {
+        //                fResult.append("</li>\n<li>");
+        //              } else {
+        //                fTokenStack.push(LIST_OL_START);
+        //                fResult.append("\n<ol>\n<li>");
+        //              }
+        //              break;
+        //            case TokenLIST_OL_END :
+        //              fTokenStack.pop();
+        //              fResult.append("</li>\n</ol>\n");
+        //              break;
+        }
+      }
+    } catch (InvalidInputException e) {
+      //
+    }
+    // clear rest of stack if necessary (case of error in syntax!?)
+    AbstractTag tok;
+    while ((tok = (AbstractTag) fTokenStack.pop()) != WikipediaFilter.START) {
+      if (tok instanceof OpenTagToken) {
+
+        CloseTagToken closeToken = (CloseTagToken) WikipediaFilter.CLOSE_TAGS.get(tok.getTagName());
+        if (closeToken == null) {
+          // here is something wrong ???
+          fResultBuffer.append("</" + (tok.getTagName()) + ">");
+        } else {
+          fResultBuffer.append(closeToken.getCloseTag());
+        }
+      } else if (tok == WikipediaFilter.BOLD) {
+        fResultBuffer.append("</b>");
+      } else if (tok == WikipediaFilter.ITALIC) {
+        fResultBuffer.append("</i>");
+      } else if (tok == WikipediaFilter.STRONG) {
+        fResultBuffer.append("</strong>");
+      } else if (tok == WikipediaFilter.EM) {
+        fResultBuffer.append("</em>");
+      } else if (tok == WikipediaFilter.STRIKETHROUGH) {
+        fResultBuffer.append("</strike>");
+      } else if (tok.equals(WikipediaFilter.LIST_UL_START)) {
+        fResultBuffer.append("</li>\n</ul>\n");
+      } else if (tok.equals(WikipediaFilter.LIST_OL_START)) {
+        fResultBuffer.append("</li>\n</ol>\n");
+      }
+    }
+
+    if (fResultBufferHeader != null) {
+      int tocStart = fResultBufferHeader.length();
+      fResultBufferHeader.append("<table id=\"toc\" border=\"0\"><tr><th>Table of contents</th></tr><tr><td>");
+      fResultBufferHeader.append("<ol>");
+      createToC(fTableOfContent);
+      fResultBufferHeader.append("</ol>");
+      fResultBufferHeader.append("</td></tr></table><hr/>");
+
+      fResultBufferHeader.append(fResultBuffer);
+      fResultBuffer = fResultBufferHeader;
+      fResultBufferHeader = null;
+      fTableOfContent = null;
+    }
+  }
+
+  private void createToC(ArrayList toc) {
+    if (toc.size() == 1 && (toc.get(0) instanceof ArrayList)) {
+      createToC((ArrayList) toc.get(0));
+      return;
+    }
+    for (int i = 0; i < toc.size(); i++) {
+      if (toc.get(i) instanceof ArrayList) {
+        fResultBufferHeader.append("<ol>");
+        createToC((ArrayList) toc.get(i));
+        fResultBufferHeader.append("</ol>");
+      } else {
+        fResultBufferHeader.append(toc.get(i));
+      }
+    }
+  }
+
+  //  public int readUntil(String testString) throws InvalidInputException {
+  //    startPosition = currentPosition;
+  //    int tempPosition;
+  //    boolean flag;
+  //    try {
+  //      while (true) {
+  //        currentCharacter = source[currentPosition++];
+  //        if (currentCharacter == testString.charAt(0)) {
+  //          tempPosition = currentPosition;
+  //          flag = true;
+  //          for (int i = 1; i < testString.length(); i++) {
+  //            currentCharacter = source[currentPosition++];
+  //            if (currentCharacter != testString.charAt(i)) {
+  //              flag = false;
+  //              currentPosition = tempPosition;
+  //              break;
+  //            }
+  //          }
+  //          if (flag) {
+  //            return TokenBODY;
+  //          }
+  //        }
+  //      }
+  //    } catch (IndexOutOfBoundsException e) {
+  //      // end of scanner text
+  //    }
+  //    return TokenEOF;
+  //  }
+
+  public int scanIdentifierOrKeyword(boolean isVariable) throws InvalidInputException {
+    while (getNextCharAsWikiPluginIdentifierPart()) {
+    }
+    ;
+    return WikipediaFilter.TokenIdentifier;
+  }
+
+  private final void setSource(char[] source) {
+    //the source-buffer is set to sourceString
+    if (source == null) {
+      this.fSource = new char[0];
+    } else {
+      this.fSource = source;
+    }
+    //      this.fEOFPosition = this.fSource.length;
+    //      fStartPosition = -1;
+  }
+
+  private void unexpectedTag(String tag) {
+    fResultBuffer.append("<div class=\"error\">Unexpected end for tag: &lt;" + tag + "&gt;</div>");
+  }
+
+  /**
+   * @return Returns the context.
+   */
+  public FilterContext getContext() {
+    return fContext;
+  }
+
+  /**
+   * @param context
+   *          The context to set.
+   */
+  public void setContext(FilterContext context) {
+    fContext = context;
+  }
+
+  /**
+   * @return Returns the wikiEngine.
+   */
+  public RenderEngine getWikiEngine() {
+    return fWikiEngine;
+  }
+
+  /**
+   * @param wikiEngine
+   *          The wikiEngine to set.
+   */
+  public void setWikiEngine(RenderEngine wikiEngine) {
+    fWikiEngine = wikiEngine;
+  }
+}
\ No newline at end of file