initial contribution
[phpeclipse.git] / archive / org.plog4u.wiki / src / org / plog4u / wiki / filter / WikipediaParser.java
1 package org.plog4u.wiki.filter;
2
3 import java.io.IOException;
4 import java.io.Writer;
5 import java.util.ArrayList;
6 import java.util.NoSuchElementException;
7 import java.util.Stack;
8 import java.util.StringTokenizer;
9
10 //import org.apache.commons.logging.Log;
11 //import org.apache.commons.logging.LogFactory;
12 import org.plog4u.wiki.filter.WikipediaFilter.InvalidInputException;
13 import org.plog4u.wiki.filter.tags.AbstractTag;
14 import org.plog4u.wiki.filter.tags.CloseTagToken;
15 import org.plog4u.wiki.filter.tags.ListToken;
16 import org.plog4u.wiki.filter.tags.OpenTagToken;
17 import org.plog4u.wiki.filter.tags.SpecialTagToken;
18 import org.radeox.api.engine.ImageRenderEngine;
19 import org.radeox.api.engine.IncludeRenderEngine;
20 import org.radeox.api.engine.RenderEngine;
21 import org.radeox.api.engine.WikiRenderEngine;
22 import org.radeox.filter.context.FilterContext;
23 import org.radeox.filter.interwiki.InterWiki;
24 import org.radeox.macro.Macro;
25 import org.radeox.macro.MacroRepository;
26 import org.radeox.macro.parameter.MacroParameter;
27 import org.radeox.util.Encoder;
28 import org.radeox.util.StringBufferWriter;
29
30 /**
31  * A parser for the WikipediaFilter
32  * 
33  * @see org.plog4u.wiki.filter.WikipediaFilter
34  */
35 public class WikipediaParser {
36   //  private static Log log = LogFactory.getLog(WikipediaFilter.class);
37
38   MacroRepository fMacros;
39
40   private FilterContext fContext;
41
42   private RenderEngine fWikiEngine;
43
44   // TODO check, if this counter is correct in recursions:
45   private int fImageCounter;
46
47   /**
48    * The current snip
49    */
50   //  private Snip fSnip;
51   /**
52    * If the snip contains headings for a "table of content" this buffer temporarily contains the start of the snip and the
53    * "table of content"
54    */
55   private StringBuffer fResultBufferHeader = null;
56
57   /**
58    * The buffer for the resulting HTML rendering from the current snip.
59    */
60   private StringBuffer fResultBuffer;
61
62   /**
63    * The wiki syntax string which should be parsed
64    */
65   private char[] fSource;
66
67   /**
68    * The corresponding String for the character source array
69    */
70   private final String fStringSource;
71
72   /**
73    * The current scanned character
74    */
75   private char fCurrentCharacter;
76
77   /**
78    * The current offset in the character source array
79    */
80   private int fCurrentPosition;
81
82   /**
83    * The current recursion level for this parser
84    */
85   private int fRecursionLevel;
86
87   private Stack fTokenStack;
88
89   //  private Stack fTableStack;
90
91   private boolean fWhiteStart = false;
92
93   private int fWhiteStartPosition = 0;
94
95   //  private TeXParser fTeXParser;
96   //  private TeXParser fTeXImageParser;
97   /**
98    * 
99    * "table of content"
100    *  
101    */
102   private ArrayList fTableOfContent = null;
103
104   //  private String fSrcPath;
105   //  private String fBinPath;
106
107   public WikipediaParser(MacroRepository macros, String stringSource, StringBuffer result, FilterContext context, int recursionLevel) {
108     fContext = context;
109     fWikiEngine = context.getRenderContext().getRenderEngine();
110
111     //    try {
112     //      SnipMacroParameter params = (SnipMacroParameter)
113     // fContext.getMacroParameter();
114     //      fSnip = params.getSnipRenderContext().getSnip();
115     //    } catch (ClassCastException e) {
116     //      e.printStackTrace();
117     //    }
118     fMacros = macros;
119     fResultBuffer = result;
120     fStringSource = stringSource;
121     setSource(stringSource.toCharArray());
122     fRecursionLevel = recursionLevel;
123     fTokenStack = new Stack();
124     //    fTableStack = new Stack();
125     //    fTeXParser = new TeXParser("", "m:");
126     //    fTeXImageParser = new TeXParser("", "");
127     fImageCounter = 1;
128
129     //    fSrcPath = (String) fContext.getRenderContext().get("srcpath");
130     //    if (fSrcPath==null) {
131     //      fSrcPath = "";
132     //    }
133     //    fBinPath = (String) fContext.getRenderContext().get("binpath");
134     //    if (fBinPath==null) {
135     //      fBinPath = "";
136     //    }
137   }
138
139   /**
140    * Check until a new-line was found, if there are only whitespace characters before the given endposition.
141    * 
142    * @param startPosition
143    * @param endPosition
144    * @return -1 if no whitespace line is found from the end (i.e. endPosition); otherwise the offset directly after where the
145    *         new-line was found
146    */
147   private int checkWhitespaces(int startPosition, int endPosition) {
148     char tempChar;
149     while (endPosition >= startPosition) {
150       if ((tempChar = fSource[endPosition--]) == '\n') {
151         return endPosition + 2;
152       }
153       if (tempChar != ' ' && tempChar != '\t' && tempChar != '\r') {
154         return -1;
155       }
156     }
157     if (endPosition < startPosition && endPosition >= 0) {
158       if ((tempChar = fSource[endPosition]) != '\n') {
159         return -1;
160       }
161     } else if (endPosition == (-1) && startPosition == 0) {
162       // special case at the start of a string
163       return 0;
164     }
165     return startPosition;
166   }
167
168   /**
169    * copy the content in the resulting buffer and escape special html characters (&lt; &gt; &quot; &amp; &#39;)
170    */
171   private void copyWhite(boolean whiteStart, final int whiteStartPosition, final int diff) {
172     if (whiteStart) {
173       final int len = fCurrentPosition - diff;
174       int currentIndex = whiteStartPosition;
175       int lastIndex = currentIndex;
176       while (currentIndex < len) {
177         switch (fSource[currentIndex++]) {
178         case '<': // special html escape character
179           if (lastIndex < (currentIndex - 1)) {
180             fResultBuffer.append(fSource, lastIndex, currentIndex - lastIndex - 1);
181             lastIndex = currentIndex;
182           } else {
183             lastIndex++;
184           }
185           fResultBuffer.append("&#60;");
186           break;
187         case '>': // special html escape character
188           if (lastIndex < (currentIndex - 1)) {
189             fResultBuffer.append(fSource, lastIndex, currentIndex - lastIndex - 1);
190             lastIndex = currentIndex;
191           } else {
192             lastIndex++;
193           }
194           fResultBuffer.append("&#62;");
195           break;
196         case '&': // special html escape character
197           if (lastIndex < (currentIndex - 1)) {
198             fResultBuffer.append(fSource, lastIndex, currentIndex - lastIndex - 1);
199             lastIndex = currentIndex;
200           } else {
201             lastIndex++;
202           }
203           fResultBuffer.append("&#38;");
204           break;
205         case '\'': // special html escape character
206           if (lastIndex < (currentIndex - 1)) {
207             fResultBuffer.append(fSource, lastIndex, currentIndex - lastIndex - 1);
208             lastIndex = currentIndex;
209           } else {
210             lastIndex++;
211           }
212           fResultBuffer.append("&#39;");
213           break;
214         case '\"': // special html escape character
215           if (lastIndex < (currentIndex - 1)) {
216             fResultBuffer.append(fSource, lastIndex, currentIndex - lastIndex - 1);
217             lastIndex = currentIndex;
218           } else {
219             lastIndex++;
220           }
221           fResultBuffer.append("&#34;");
222           break;
223         }
224       }
225       if (lastIndex < (currentIndex)) {
226         fResultBuffer.append(fSource, lastIndex, currentIndex - lastIndex);
227       }
228       fWhiteStart = false;
229     }
230   }
231
232   /**
233    * copy the text in the resulting buffer and escape special html characters (&lt; &gt; &quot; &amp; &#39;)
234    */
235   private void copyWhite(String text) {
236     final int len = text.length();
237     int currentIndex = 0;
238     int lastIndex = currentIndex;
239     while (currentIndex < len) {
240       switch (text.charAt(currentIndex++)) {
241       case '<': // special html escape character
242         if (lastIndex < (currentIndex - 1)) {
243           fResultBuffer.append(text.substring(lastIndex, currentIndex - 1));
244           lastIndex = currentIndex;
245         }
246         fResultBuffer.append("&#60;");
247         break;
248       case '>': // special html escape character
249         if (lastIndex < (currentIndex - 1)) {
250           fResultBuffer.append(text.substring(lastIndex, currentIndex - 1));
251           lastIndex = currentIndex;
252         } else {
253           lastIndex++;
254         }
255         fResultBuffer.append("&#62;");
256         break;
257       case '&': // special html escape character
258         if (lastIndex < (currentIndex - 1)) {
259           fResultBuffer.append(text.substring(lastIndex, currentIndex - 1));
260           lastIndex = currentIndex;
261         } else {
262           lastIndex++;
263         }
264         fResultBuffer.append("&#38;");
265         break;
266       case '\'': // special html escape character
267         if (lastIndex < (currentIndex - 1)) {
268           fResultBuffer.append(text.substring(lastIndex, currentIndex - 1));
269           lastIndex = currentIndex;
270         } else {
271           lastIndex++;
272         }
273         fResultBuffer.append("&#39;");
274         break;
275       case '\"': // special html escape character
276         if (lastIndex < (currentIndex - 1)) {
277           fResultBuffer.append(text.substring(lastIndex, currentIndex - 1));
278           lastIndex = currentIndex;
279         } else {
280           lastIndex++;
281         }
282         fResultBuffer.append("&#34;");
283         break;
284       }
285     }
286     if (lastIndex < (currentIndex)) {
287       fResultBuffer.append(text.substring(lastIndex, currentIndex));
288     }
289   }
290
291   /**
292    * Copy the text in the resulting buffer and escape special html characters (&lt; &gt; &quot; &amp; &#39;) Additionally every
293    * newline will be replaced by &lt;br/&gt;
294    */
295   private void copyNowikiNewLine(String text) {
296     final int len = text.length();
297     int currentIndex = 0;
298     int lastIndex = currentIndex;
299     while (currentIndex < len) {
300       switch (text.charAt(currentIndex++)) {
301       case '\n':
302         if (lastIndex < (currentIndex - 1)) {
303           fResultBuffer.append(text.substring(lastIndex, currentIndex - 1));
304           lastIndex = currentIndex;
305         } else {
306           lastIndex++;
307         }
308         fResultBuffer.append("<br/>");
309         break;
310       case '<': // special html escape character
311         if (lastIndex < (currentIndex - 1)) {
312           fResultBuffer.append(text.substring(lastIndex, currentIndex - 1));
313           lastIndex = currentIndex;
314         } else {
315           lastIndex++;
316         }
317         fResultBuffer.append("&#60;");
318         break;
319       case '>': // special html escape character
320         if (lastIndex < (currentIndex - 1)) {
321           fResultBuffer.append(text.substring(lastIndex, currentIndex - 1));
322           lastIndex = currentIndex;
323         } else {
324           lastIndex++;
325         }
326         fResultBuffer.append("&#62;");
327         break;
328       //      case '&': // special html escape character
329       //        if (lastIndex < (currentIndex - 1)) {
330       //          fResultBuffer.append(text.substring(lastIndex, currentIndex - 1));
331       //          lastIndex = currentIndex;
332       //        } else {
333       //          lastIndex++;
334       //        }
335       //        fResultBuffer.append("&#38;");
336       //        break;
337       case '\'': // special html escape character
338         if (lastIndex < (currentIndex - 1)) {
339           fResultBuffer.append(text.substring(lastIndex, currentIndex - 1));
340           lastIndex = currentIndex;
341         } else {
342           lastIndex++;
343         }
344         fResultBuffer.append("&#39;");
345         break;
346       case '\"': // special html escape character
347         if (lastIndex < (currentIndex - 1)) {
348           fResultBuffer.append(text.substring(lastIndex, currentIndex - 1));
349           lastIndex = currentIndex;
350         } else {
351           lastIndex++;
352         }
353         fResultBuffer.append("&#34;");
354         break;
355       }
356     }
357     if (lastIndex < (currentIndex)) {
358       fResultBuffer.append(text.substring(lastIndex, currentIndex));
359     }
360   }
361
362   /**
363    * Render the HTML token which are defined in the OPEN_TAGS and CLOSE_TAGS map
364    * 
365    * @return
366    */
367   public int getHTMLToken() {
368     int currentHtmlPosition = fCurrentPosition;
369     try {
370       char closeCharacter;
371       char nextCharacter;
372       if (getNextChar('/')) {
373         currentHtmlPosition++;
374         // closing tag
375         int r = readUntilCharOrEOL('>');
376         if (r != 1) {
377           return WikipediaFilter.TokenNotFound;
378         }
379         String closeTagString = new String(fSource, currentHtmlPosition, fCurrentPosition - currentHtmlPosition - 1).toLowerCase();
380         //          System.out.println(closeTagString);
381         StringTokenizer tagTokenizer = new StringTokenizer(closeTagString);
382         String tokenString;
383         try {
384           tokenString = tagTokenizer.nextToken();
385           CloseTagToken token = (CloseTagToken) WikipediaFilter.CLOSE_TAGS.get(tokenString);
386           if (token == null) {
387             return WikipediaFilter.TokenNotFound;
388           }
389           Object topToken = fTokenStack.peek();
390           if (topToken instanceof OpenTagToken && ((OpenTagToken) topToken).getTagName() == token.getTagName()) {
391             fTokenStack.pop();
392             //            if (token.getTagName().equals("table")) {
393             //              fTableStack.pop();
394             //            }
395             copyWhite(fWhiteStart, fWhiteStartPosition, 3 + tokenString.length());
396             fWhiteStart = false;
397             fResultBuffer.append(token.getCloseTag());
398             return WikipediaFilter.TokenIgnore;
399           }
400           fWhiteStart = false;
401           unexpectedTag(token.getTagName());
402           return WikipediaFilter.TokenIgnore;
403         } catch (NoSuchElementException e) {
404           return WikipediaFilter.TokenNotFound;
405         }
406
407       } else {
408         // opening tag
409         String tokenString;
410         int tagNameStart = fCurrentPosition;
411         int tokenLength = 0;
412         while (Character.isJavaIdentifierStart(fSource[fCurrentPosition++])) {
413           tokenLength++;
414         }
415         try {
416           tokenString = new String(fSource, tagNameStart, fCurrentPosition - tagNameStart - 1); //tagTokenizer.nextToken();
417           OpenTagToken token = (OpenTagToken) WikipediaFilter.OPEN_TAGS.get(tokenString);
418           if (token == null) {
419             return WikipediaFilter.TokenNotFound;
420           }
421           copyWhite(fWhiteStart, fWhiteStartPosition, (fCurrentPosition - tagNameStart) + 1);
422           fWhiteStart = false;
423
424           if (token instanceof SpecialTagToken) {
425             fResultBuffer.append(token.getOpenTag());
426             while (Character.isWhitespace(fSource[fCurrentPosition])) {
427               fCurrentPosition++;
428             }
429             if (fSource[fCurrentPosition] == '/') {
430               fCurrentPosition++;
431             }
432             if (fSource[fCurrentPosition] == '>') {
433               fCurrentPosition++;
434             }
435           } else if (token instanceof OpenTagToken) {
436             fResultBuffer.append("<");
437             fResultBuffer.append(token.getTagName());
438             fTokenStack.push(token);
439             fCurrentPosition = token.scan(fResultBuffer, fSource, fCurrentPosition - 1);
440             fResultBuffer.append(">");
441           }
442
443           //                                    System.out.println(fResultBuffer);
444           return WikipediaFilter.TokenIgnore;
445         } catch (NoSuchElementException e) {
446           return WikipediaFilter.TokenNotFound;
447         }
448       }
449
450     } catch (IndexOutOfBoundsException e) {
451       //
452     }
453     fCurrentPosition = currentHtmlPosition;
454     return WikipediaFilter.TokenNotFound;
455   }
456
457   public final boolean getNextChar(char testedChar) {
458     int temp = fCurrentPosition;
459     try {
460       fCurrentCharacter = fSource[fCurrentPosition++];
461       if (fCurrentCharacter != testedChar) {
462         fCurrentPosition = temp;
463         return false;
464       }
465       return true;
466
467     } catch (IndexOutOfBoundsException e) {
468       fCurrentPosition = temp;
469       return false;
470     }
471   }
472
473   public final int getNextChar(char testedChar1, char testedChar2) {
474     int temp = fCurrentPosition;
475     try {
476       int result;
477       fCurrentCharacter = fSource[fCurrentPosition++];
478       if (fCurrentCharacter == testedChar1)
479         result = 0;
480       else if (fCurrentCharacter == testedChar2)
481         result = 1;
482       else {
483         fCurrentPosition = temp;
484         return -1;
485       }
486       return result;
487     } catch (IndexOutOfBoundsException e) {
488       fCurrentPosition = temp;
489       return -1;
490     }
491   }
492
493   public final boolean getNextCharAsDigit() {
494     int temp = fCurrentPosition;
495     try {
496       fCurrentCharacter = fSource[fCurrentPosition++];
497       if (!Character.isDigit(fCurrentCharacter)) {
498         fCurrentPosition = temp;
499         return false;
500       }
501       return true;
502     } catch (IndexOutOfBoundsException e) {
503       fCurrentPosition = temp;
504       return false;
505     }
506   }
507
508   public final boolean getNextCharAsDigit(int radix) {
509
510     int temp = fCurrentPosition;
511     try {
512       fCurrentCharacter = fSource[fCurrentPosition++];
513
514       if (Character.digit(fCurrentCharacter, radix) == -1) {
515         fCurrentPosition = temp;
516         return false;
517       }
518       return true;
519     } catch (IndexOutOfBoundsException e) {
520       fCurrentPosition = temp;
521       return false;
522     }
523   }
524
525   public final int getNumberOfChar(char testedChar) {
526     int number = 0;
527     try {
528       while ((fCurrentCharacter = fSource[fCurrentPosition++]) == testedChar) {
529         number++;
530       }
531     } catch (IndexOutOfBoundsException e) {
532
533     }
534     fCurrentPosition--;
535     return number;
536   }
537
538   public final char[] getListChars() {
539
540     int startPosition = fCurrentPosition - 1;
541     try {
542       while (true) {
543         fCurrentCharacter = fSource[fCurrentPosition++];
544         if (fCurrentCharacter != '*' && fCurrentCharacter != '#') {
545           break;
546         }
547       }
548     } catch (IndexOutOfBoundsException e) {
549       //
550     }
551     fCurrentPosition--;
552     char[] result = new char[fCurrentPosition - startPosition];
553     System.arraycopy(fSource, startPosition, result, 0, fCurrentPosition - startPosition);
554     return result;
555   }
556
557   public boolean getNextCharAsWikiPluginIdentifierPart() {
558     int temp = fCurrentPosition;
559     try {
560       fCurrentCharacter = fSource[fCurrentPosition++];
561
562       if (!WikipediaFilter.isWikiPluginIdentifierPart(fCurrentCharacter)) {
563         fCurrentPosition = temp;
564         return false;
565       }
566       return true;
567     } catch (IndexOutOfBoundsException e) {
568       fCurrentPosition = temp;
569       return false;
570     }
571   }
572
573   private void stopList() {
574     while (!fTokenStack.isEmpty()) {
575       AbstractTag tok = (AbstractTag) fTokenStack.peek();
576       if (tok.equals(WikipediaFilter.LIST_UL_START)) {
577         fTokenStack.pop();
578         fResultBuffer.append("</li></ul>");
579       } else if (tok.equals(WikipediaFilter.LIST_OL_START)) {
580         fTokenStack.pop();
581         fResultBuffer.append("</li></ol>");
582       } else if (tok == WikipediaFilter.BOLD) {
583         fTokenStack.pop();
584         fResultBuffer.append("</b>");
585       } else if (tok == WikipediaFilter.ITALIC) {
586         fTokenStack.pop();
587         fResultBuffer.append("</i>");
588       } else if (tok == WikipediaFilter.STRONG) {
589         fTokenStack.pop();
590         fResultBuffer.append("</strong>");
591       } else if (tok == WikipediaFilter.EM) {
592         fTokenStack.pop();
593         fResultBuffer.append("</em>");
594       } else if (tok == WikipediaFilter.STRIKETHROUGH) {
595         fTokenStack.pop();
596         fResultBuffer.append("</strike>");
597       } else {
598         break;
599       }
600     }
601   }
602
603   protected int getNextToken() throws InvalidInputException {
604     boolean startOfIndent = false;
605     fWhiteStartPosition = 0;
606     fWhiteStart = false;
607     try {
608       while (true) {
609         //          fStartPosition = fCurrentPosition;
610         fCurrentCharacter = fSource[fCurrentPosition++];
611
612         // ---------Identify the next token-------------
613         switch (fCurrentCharacter) {
614         case '\n':
615           if (fWhiteStart) {
616             int tempPosition = checkWhitespaces(fWhiteStartPosition, fCurrentPosition - 2);
617             if (tempPosition >= 0) {
618               copyWhite(fWhiteStart, fWhiteStartPosition, fCurrentPosition - (++tempPosition));
619               fWhiteStart = false;
620               stopList();
621               fResultBuffer.append("<p/>");
622               //                                                        continue;
623             }
624
625           }
626           int fStartPrePosition = fCurrentPosition;
627           boolean preSection = false;
628           try {
629             while (fSource[fCurrentPosition++] == ' ') {
630               fCurrentCharacter = fSource[fCurrentPosition++];
631               while (fCurrentCharacter != '\n') {
632                 if (!Character.isWhitespace(fCurrentCharacter)) {
633                   //                                                                     preformatted section starts here
634                   preSection = true;
635                 }
636                 fCurrentCharacter = fSource[fCurrentPosition++];
637               }
638             }
639             --fCurrentPosition;
640           } catch (IndexOutOfBoundsException e) {
641
642           }
643           if (preSection && fRecursionLevel == 1) {
644             String preString;
645             copyWhite(fWhiteStart, fStartPrePosition, fCurrentPosition - fStartPrePosition);
646             fWhiteStart = true;
647             fResultBuffer.append("<pre>");
648             //            copyWhite(fWhiteStart, fStartPrePosition, 1);
649             preString = new String(fSource, fStartPrePosition, fCurrentPosition - fStartPrePosition - 1) + '\n';
650             fResultBuffer.append(WikipediaFilter.filterParser(preString, fContext, fMacros, fRecursionLevel));
651             //            preString = new String(fSource, fStartPrePosition, fCurrentPosition - fStartPrePosition - 1)+'\n';
652             //            int preIndex = 0;
653             //            int lastIndex = 0;
654             //            while (preIndex>=0) {
655             //              preIndex = preString.indexOf('\n', lastIndex);
656             //              if (preIndex>=0) {
657             //                fResultBuffer.append(WikipediaFilter.filterParser(preString.substring(lastIndex,preIndex), fContext,
658             // fCachedPage, fMacros, fRecursionLevel));
659             //                fResultBuffer.append('\n');
660             //                lastIndex = ++preIndex;
661             //              }
662             //            }
663             fResultBuffer.append("</pre>");
664             fWhiteStart = false;
665             continue;
666           } else {
667             fCurrentPosition = fStartPrePosition;
668           }
669           break;
670         case ':':
671           if (isStartOfLine()) {
672             copyWhite(fWhiteStart, fWhiteStartPosition, 1);
673             fWhiteStart = false;
674
675             int levelHeader = getNumberOfChar(':') + 1;
676             int startHeadPosition = fCurrentPosition;
677             if (readUntilEOL()) {
678               String head = new String(fSource, startHeadPosition, fCurrentPosition - startHeadPosition);
679               for (int i = 0; i < levelHeader; i++) {
680                 fResultBuffer.append("<dl><dd>");
681               }
682               fResultBuffer.append(head);
683               for (int i = 0; i < levelHeader; i++) {
684                 fResultBuffer.append("</dd></dl>");
685               }
686               continue;
687             }
688
689             continue;
690           }
691           break;
692         case ';':
693           if (isStartOfLine() && getNextChar(' ')) {
694             copyWhite(fWhiteStart, fWhiteStartPosition, 1);
695             fWhiteStart = false;
696
697             int startHeadPosition = fCurrentPosition;
698             if (readUntilEOL()) {
699               // TODO not correct - improve this
700               String head = new String(fSource, startHeadPosition, fCurrentPosition - startHeadPosition);
701               int index = head.indexOf(": ");
702               if (index > 0) {
703                 fResultBuffer.append("<dl><dt>");
704                 fResultBuffer.append(head.substring(0,index));
705                 fResultBuffer.append("</dt><dd>");
706                 fResultBuffer.append(head.substring(index+2));
707                 fResultBuffer.append("</dd></dl>");
708               } else {
709                 fResultBuffer.append("<dl><dt>");
710                 fResultBuffer.append(head);
711                 fResultBuffer.append("</dt></dl>");
712               }
713               continue;
714             }
715
716             continue;
717           }
718           break;
719         //                              case '\\': // special characters follow
720         //                                      copyWhite(fWhiteStart, fWhiteStartPosition, 1);
721         //                                      fWhiteStart = false;
722         //                                      try {
723         //                                              fCurrentCharacter = fSource[fCurrentPosition++];
724         //                                              switch (fCurrentCharacter) {
725         //                                              case '\\': // newline
726         //                                                      if ((fCurrentCharacter = fSource[fCurrentPosition++]) == '\\') {
727         //                                                              fResultBuffer.append(Encoder
728         //                                                                              .toEntity(fCurrentCharacter));
729         //                                                              break;
730         //                                                      } else {
731         //                                                              fResultBuffer.append("<br />");
732         //                                                              break;
733         //                                                      }
734         //                                              default:
735         //                                                      fResultBuffer.append(Encoder
736         //                                                                      .toEntity(fCurrentCharacter));
737         //                                              }
738         //                                      } catch (IndexOutOfBoundsException e) {
739         //
740         //                                      }
741         //                                      continue;
742         //          case '$' : // detect tex math
743         //            copyWhite(fWhiteStart, fWhiteStartPosition, 1);
744         //            fWhiteStart = false;
745         //            startOfIndent = false;
746         //            int startMathPosition = fCurrentPosition;
747         //            if (getNextChar('$')) {
748         //              startMathPosition = fCurrentPosition;
749         //              copyWhite(fWhiteStart, fWhiteStartPosition, 2);
750         //              fWhiteStart = false;
751         //              if (readUntilString("$$")) {
752         //                String mathContent = new String(fSource, startMathPosition,
753         // fCurrentPosition - startMathPosition - 2);
754         //                if (mathContent != null) {
755         //                  handleTeXMath(mathContent, false);
756         //                  continue;
757         //                }
758         //              }
759         //            } else {
760         //              if (readUntilChar('$')) {
761         //                String mathContent = new String(fSource, startMathPosition,
762         // fCurrentPosition - startMathPosition - 1);
763         //                if (mathContent != null) {
764         //                  handleTeXMath(mathContent, true);
765         //                  continue;
766         //                }
767         //              }
768         //            }
769         //            break;
770         case '{':
771           // detect macros
772           copyWhite(fWhiteStart, fWhiteStartPosition, 1);
773           fWhiteStart = false;
774           //              boolean scanBody = true;
775           int startMacroPosition = fCurrentPosition;
776           if (getNextChar('|') && handleWikipediaTable()) { // Wikipedia
777             // table
778             // syntax
779             continue;
780           } else {
781             if (readUntilChar('}')) {
782               String macroStartTag;
783
784               macroStartTag = new String(fSource, startMacroPosition, fCurrentPosition - startMacroPosition - 1);
785               if (macroStartTag != null) {
786                 createMacro(startMacroPosition, macroStartTag);
787                 continue;
788               }
789             }
790           }
791           break;
792         case '[':
793           int startLinkPosition = fCurrentPosition;
794           if (getNextChar('[')) { // wikipedia link style
795             startLinkPosition = fCurrentPosition;
796             copyWhite(fWhiteStart, fWhiteStartPosition, 2);
797             fWhiteStart = false;
798             if (readUntilString("]]")) {
799               String name = new String(fSource, startLinkPosition, fCurrentPosition - startLinkPosition - 2);
800               // test for suffix string
801               int temp = fCurrentPosition;
802               StringBuffer suffixBuffer = new StringBuffer();
803               try {
804                 while (true) {
805                   fCurrentCharacter = fSource[fCurrentPosition++];
806                   if (!Character.isLetterOrDigit(fCurrentCharacter)) {
807                     fCurrentPosition--;
808                     break;
809                   }
810                   suffixBuffer.append(fCurrentCharacter);
811                 }
812                 handleWikipediaLink(name, suffixBuffer.toString());
813                 continue;
814               } catch (IndexOutOfBoundsException e) {
815                 fCurrentPosition = temp;
816               }
817
818               handleWikipediaLink(name, "");
819               continue;
820             }
821
822           } else {
823             copyWhite(fWhiteStart, fWhiteStartPosition, 1);
824             fWhiteStart = false;
825
826             if (readUntilChar(']')) {
827               String name = new String(fSource, startLinkPosition, fCurrentPosition - startLinkPosition - 1);
828               handleSnipLink(name);
829               continue;
830             }
831           }
832           break;
833         //                              case '1': // heading filter ?
834         //                                      int temp1Position = checkWhitespaces(fWhiteStartPosition,
835         //                                                      fCurrentPosition - 2);
836         //                                      if (temp1Position >= 0) {
837         //                                              copyWhite(fWhiteStart, fWhiteStartPosition, 1);
838         //                                              fWhiteStart = false;
839         //                                              int simpleHeader = getNextChar(' ', '.');
840         //                                              if (simpleHeader < 0) {
841         //                                                      if (getNextChar('1')) {
842         //                                                              fCurrentPosition--;
843         //                                                              if (getList('1', "<ol>", "</ol>")) {
844         //                                                                      continue;
845         //                                                              }
846         //                                                      }
847         //                                                      break;
848         //                                              }
849         //                                              if (simpleHeader == 1 && !getNextChar('1')) {
850         //                                                      fCurrentPosition--;
851         //                                                      if (getList('1', "<ol>", "</ol>")) {
852         //                                                              continue;
853         //                                                      }
854         //                                                      break;
855         //                                              }
856         //                                              temp1Position = fCurrentPosition;
857         //                                              if (simpleHeader >= 0 && readUntilChar('\n')) {
858         //                                                      String heading = new String(fSource, temp1Position,
859         //                                                                      fCurrentPosition - temp1Position - 1);
860         //                                                      if (heading != null) {
861         //                                                              fResultBuffer.append("<h3 class=\"heading-");
862         //                                                              if (simpleHeader == 1) {
863         //                                                                      fResultBuffer.append("1");
864         //                                                              } else {
865         //                                                                      fResultBuffer.append("1-1");
866         //                                                              }
867         //                                                              fResultBuffer.append("\">");
868         //                                                              // System.out.println(heading);
869         //                                                              fResultBuffer
870         //                                                                              .append(WikipediaFilter
871         //                                                                                              .filterParser(
872         //                                                                                                              heading,
873         //                                                                                                              fContext,
874         //                                                                                                              WikipediaFilter.DUMMY_CACHED_PAGE,
875         //                                                                                                              fMacros,
876         //                                                                                                              fRecursionLevel));
877         //                                                              fResultBuffer.append("</h3>");
878         //                                                              continue;
879         //                                                      }
880         //                                              }
881         //                                      }
882         //                                      break;
883         case '*': // <ul> list
884         case '#': // <ol> list
885           if (isStartOfLine()) {
886             char[] listChars = getListChars();
887             int tempStarPosition = checkWhitespaces(fWhiteStartPosition, fCurrentPosition - 1 - listChars.length);
888             if (tempStarPosition >= 0) {
889               appendList(listChars);
890               continue;
891             }
892           }
893           break;
894         //        case '#': // <ol> list
895         //          if (fCurrentPosition >= 2) {
896         //            char beforeChar = fSource[fCurrentPosition - 2];
897         //            if (beforeChar == '\n' || beforeChar == '\r') {
898         //
899         //              int levelHash = getNumberOfChar('#') + 1;
900         //
901         //              int tempHashPosition = checkWhitespaces(fWhiteStartPosition, fCurrentPosition - 1 - levelHash);
902         //              if (tempHashPosition >= 0) {
903         //                copyWhite(fWhiteStart, fWhiteStartPosition, levelHash);
904         //                fWhiteStart = false;
905         //                AbstractTag tok = (AbstractTag) fTokenStack.peek();
906         //                if (tok instanceof ListToken) {
907         //                  ListToken listToken = (ListToken) tok;
908         //                  int topLevel = listToken.getLevel();
909         //                  if (listToken.getToken() == WikipediaFilter.TokenLIST_OL_START) {
910         //                    if (levelHash > topLevel) {
911         //                      fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_OL_START, topLevel + 1));
912         //                      fResultBuffer.append("<ol><li>");
913         //                    } else if (levelHash < topLevel) {
914         //                      fTokenStack.pop();
915         //                      fResultBuffer.append("</li></ol></li><li>");
916         //                    } else {
917         //                      fResultBuffer.append("</li><li>");
918         //                    }
919         //                  } else {
920         //                    fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_OL_START, levelHash));
921         //                    fResultBuffer.append("<ol><li>");
922         //                  }
923         //                } else {
924         //                  fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_OL_START, 1));
925         //                  fResultBuffer.append("\n<ol><li>");
926         //                }
927         //                continue;
928         //              }
929         //            }
930         //            // }
931         //          }
932         //          break;
933
934         //                              case 'i': // <ol> list
935         //                                      if (getList('i', "<ol class=\"roman\">", "</ol>")) {
936         //                                              continue;
937         //                                      }
938         //                                      break;
939         //                              case 'I': // <ol> list
940         //                                      if (getList('i', "<ol class=\"ROMAN\">", "</ol>")) {
941         //                                              continue;
942         //                                      }
943         //                                      break;
944         //            case 'a' : // <ol> list
945         //              if (getList('a', "<ol class=\"alpha\">", "</ol>")) {
946         //                continue;
947         //              }
948         //              break;
949         //            case 'A' : // <ol> list
950         //              if (getList('A', "<ol class=\"ALPHA\">", "</ol>")) {
951         //                continue;
952         //              }
953         //              break;
954         //            case 'g' : // <ol> list
955         //              if (getList('g', "<ol class=\"greek\">", "</ol>")) {
956         //                continue;
957         //              }
958         //              break;
959         //            case 'H' : // <ol> list
960         //              if (getList('H', "<ol class=\"HIRAGANA\">", "</ol>")) {
961         //                continue;
962         //              }
963         //              break;
964         //            case 'k' : // <ol> list
965         //              if (getList('k', "<ol class=\"katakana\">", "</ol>")) {
966         //                continue;
967         //              }
968         //              break;
969         //            case 'K' : // <ol> list
970         //              if (getList('K', "<ol class=\"KATAKANA\">", "</ol>")) {
971         //                continue;
972         //              }
973         //              break;
974         //            case 'j' : // <ol> list
975         //              if (getList('j', "<ol class=\"HEBREW\">", "</ol>")) {
976         //                continue;
977         //              }
978         //              break;
979
980         case '\'':
981           if (getNextChar('\'')) {
982             if (getNextChar('\'')) {
983               copyWhite(fWhiteStart, fWhiteStartPosition, 3);
984               fWhiteStart = false;
985               return WikipediaFilter.TokenSTRONG;
986             }
987             copyWhite(fWhiteStart, fWhiteStartPosition, 2);
988             fWhiteStart = false;
989             return WikipediaFilter.TokenEM;
990           }
991           break;
992         //                              case '_':
993         //                                      if (getNextChar('_')) {
994         //                                              copyWhite(fWhiteStart, fWhiteStartPosition, 2);
995         //                                              fWhiteStart = false;
996         //                                              return WikipediaFilter.TokenBOLD;
997         //                                      }
998         //                                      break;
999         //                              case '~':
1000         //                                      if (getNextChar('~')) {
1001         //                                              copyWhite(fWhiteStart, fWhiteStartPosition, 2);
1002         //                                              fWhiteStart = false;
1003         //                                              return WikipediaFilter.TokenITALIC;
1004         //                                      }
1005         //                                      break;
1006         case '-':
1007           int tempCurrPosition = fCurrentPosition;
1008           try {
1009             if (fSource[tempCurrPosition++] == '-' && fSource[tempCurrPosition++] == '-' && fSource[tempCurrPosition++] == '-') {
1010               if (fSource[tempCurrPosition] == '\n') {
1011                 fCurrentPosition = tempCurrPosition;
1012                 fResultBuffer.append("<hr/>");
1013                 fWhiteStart = false;
1014                 continue;
1015               } else if (fSource[tempCurrPosition++] == '\r' && fSource[tempCurrPosition++] == '\n') {
1016                 fCurrentPosition = tempCurrPosition - 1;
1017                 fResultBuffer.append("<hr/>");
1018                 fWhiteStart = false;
1019                 continue;
1020               }
1021             }
1022           } catch (IndexOutOfBoundsException e) {
1023
1024           }
1025
1026           //                                    int levelMinus = getNumberOfChar('-') + 1;
1027           //                                    if (getNextChar(' ')) {
1028           //                                            int tempPosition = checkWhitespaces(
1029           //                                                            fWhiteStartPosition, fCurrentPosition - 2
1030           //                                                                            - levelMinus);
1031           //                                            if (tempPosition >= 0) {
1032           //                                                    copyWhite(fWhiteStart, fWhiteStartPosition,
1033           //                                                                    1 + levelMinus);
1034           //                                                    fWhiteStart = false;
1035           //                                                    AbstractTag tok = (AbstractTag) fTokenStack.peek();
1036           //                                                    if (tok instanceof ListToken) {
1037           //                                                            ListToken listToken = (ListToken) tok;
1038           //                                                            int topLevel = listToken.getLevel();
1039           //                                                            if (listToken.getToken() ==
1040           // WikipediaFilter.TokenLIST_UL_START) {
1041           //                                                                    if (levelMinus > topLevel) {
1042           //                                                                            fTokenStack
1043           //                                                                                            .push(new ListToken(
1044           //                                                                                                            WikipediaFilter.TokenLIST_UL_START,
1045           //                                                                                                            topLevel + 1));
1046           //                                                                            fResultBuffer
1047           //                                                                                            .append("<ul class=\"minus\"><li>");
1048           //                                                                    } else if (levelMinus < topLevel) {
1049           //                                                                            fTokenStack.pop();
1050           //                                                                            fResultBuffer
1051           //                                                                                            .append("</li></ul></li><li>");
1052           //                                                                    } else {
1053           //                                                                            fResultBuffer.append("</li><li>");
1054           //                                                                    }
1055           //                                                            } else {
1056           //                                                                    fTokenStack
1057           //                                                                                    .push(new ListToken(
1058           //                                                                                                    WikipediaFilter.TokenLIST_UL_START,
1059           //                                                                                                    levelMinus));
1060           //                                                                    fResultBuffer
1061           //                                                                                    .append("<ul class=\"minus\"><li>");
1062           //                                                            }
1063           //                                                    } else {
1064           //                                                            fTokenStack
1065           //                                                                            .push(new ListToken(
1066           //                                                                                            WikipediaFilter.TokenLIST_UL_START,
1067           //                                                                                            1));
1068           //                                                            fResultBuffer
1069           //                                                                            .append("\n<ul class=\"minus\"><li>");
1070           //                                                    }
1071           //                                                    continue;
1072           //                                            }
1073           //                                    }
1074           //                                    if (levelMinus == 2) {
1075           //                                            copyWhite(fWhiteStart, fWhiteStartPosition, 2);
1076           //                                            fWhiteStart = false;
1077           //                                            return WikipediaFilter.TokenSTRIKETHROUGH;
1078           //                                    }
1079           break;
1080         case 'h': // http(s)://
1081           int urlStartPosition = fCurrentPosition;
1082           boolean foundUrl = false;
1083           int diff = 7;
1084           try {
1085             String urlString = fStringSource.substring(fCurrentPosition - 1, fCurrentPosition + 3);
1086             if (urlString.equals("http")) {
1087               fCurrentPosition += 3;
1088               fCurrentCharacter = fSource[fCurrentPosition++];
1089               if (fCurrentCharacter == 's') { // optional
1090                 fCurrentCharacter = fSource[fCurrentPosition++];
1091                 diff++;
1092               }
1093
1094               if (fCurrentCharacter == ':' && fSource[fCurrentPosition++] == '/' && fSource[fCurrentPosition++] == '/') {
1095                 copyWhite(fWhiteStart, fWhiteStartPosition, diff);
1096                 fWhiteStart = false;
1097                 foundUrl = true;
1098                 while (WikipediaFilter.isUrlIdentifierPart(fSource[fCurrentPosition++])) {
1099                 }
1100               }
1101             }
1102           } catch (IndexOutOfBoundsException e) {
1103             if (!foundUrl) {
1104               //              rollback work :-)
1105               fCurrentPosition = urlStartPosition;
1106             }
1107           }
1108           if (foundUrl) {
1109             String urlString = new String(fSource, urlStartPosition - 1, fCurrentPosition - urlStartPosition);
1110             fCurrentPosition--;
1111             WikipediaFilter.createExternalLink(fResultBuffer, fWikiEngine, urlString);
1112             continue;
1113           }
1114           break;
1115
1116         //        case '@': // images @xml@ -> /static/rss-small.png
1117         //          copyWhite(fWhiteStart, fWhiteStartPosition, 1);
1118         //          fWhiteStart = false;
1119         //          int atStart = fCurrentPosition;
1120         //          if (readUntilChar('@')) {
1121         //            String imageTag = new String(fSource, atStart, fCurrentPosition - atStart - 1);
1122         //            if (imageTag != null) {
1123         //              if (WikipediaFilter.createStaticImage(imageTag, fResultBuffer)) {
1124         //                continue;
1125         //              }
1126         //            }
1127         //          }
1128         //          fCurrentPosition = atStart;
1129         //          break;
1130         case '&':
1131           int ampersandStart = fCurrentPosition - 1;
1132           if (getNextChar('#')) {
1133             try {
1134               StringBuffer num = new StringBuffer(5);
1135               char ch = fSource[fCurrentPosition++];
1136               while (Character.isDigit(ch)) {
1137                 num.append(ch);
1138                 ch = fSource[fCurrentPosition++];
1139               }
1140               if (num.length() > 0 && ch == ';') {
1141                 Integer i = Integer.valueOf(num.toString());
1142                 if (i.intValue() < 65536) {
1143                   copyWhite(fWhiteStart, fWhiteStartPosition, 3 + num.length());
1144                   fWhiteStart = false;
1145                   fResultBuffer.append(fSource, ampersandStart, fCurrentPosition - ampersandStart);
1146                   continue;
1147                 }
1148               }
1149             } catch (IndexOutOfBoundsException e) {
1150               // ignore exception
1151             } catch (NumberFormatException e) {
1152               // ignore exception
1153             }
1154           } else {
1155             try {
1156               StringBuffer entity = new StringBuffer(10);
1157               char ch = fSource[fCurrentPosition++];
1158               while (Character.isLetterOrDigit(ch)) {
1159                 entity.append(ch);
1160                 ch = fSource[fCurrentPosition++];
1161               }
1162               if (entity.length() > 0 && ch == ';') {
1163                 if (WikipediaFilter.ENTITY_SET.contains(entity.toString())) {
1164                   copyWhite(fWhiteStart, fWhiteStartPosition, 2 + entity.length());
1165                   fWhiteStart = false;
1166                   fResultBuffer.append(fSource, ampersandStart, fCurrentPosition - ampersandStart);
1167                   continue;
1168                 }
1169               }
1170             } catch (IndexOutOfBoundsException e) {
1171               // ignore exception
1172             } catch (NumberFormatException e) {
1173               // ignore exception
1174             }
1175           }
1176           break;
1177         case '<':
1178           int htmlStartPosition = fCurrentPosition;
1179           try {
1180             switch (fStringSource.charAt(fCurrentPosition)) {
1181             case '!': // <!-- html comment -->
1182               String htmlCommentString = fStringSource.substring(fCurrentPosition - 1, fCurrentPosition + 3);
1183
1184               if (htmlCommentString.equals("<!--")) {
1185                 fCurrentPosition += 3;
1186                 if (readUntilString("-->")) {
1187                   String htmlCommentContent = new String(fSource, htmlStartPosition + 3, fCurrentPosition - htmlStartPosition - 6);
1188                   if (htmlCommentContent != null) {
1189                     copyWhite(fWhiteStart, fWhiteStartPosition, fCurrentPosition - htmlStartPosition + 1);
1190                     fWhiteStart = false;
1191                     // insert html comment for visual checks
1192                     // only:
1193                     /*
1194                      * fResultBuffer.append(" <!--"); copyWhite(htmlCommentContent); fResultBuffer.append("--> ");
1195                      */
1196                     continue;
1197                   }
1198                 }
1199               }
1200               break;
1201             //                case 'm' : // math
1202             //                  String mathString =
1203             // fStringSource.substring(fCurrentPosition - 1,
1204             // fCurrentPosition + 5);
1205
1206             //                  if (mathString.equals("<math>")) {
1207             //                    fCurrentPosition += 5;
1208             //                    if (readUntilString("</math>")) {
1209             //                      String mathContent = new String(fSource,
1210             // htmlStartPosition + 5, fCurrentPosition -
1211             // htmlStartPosition - 12);
1212             //                      if (mathContent != null) {
1213             //                        copyWhite(fWhiteStart, fWhiteStartPosition,
1214             // fCurrentPosition - htmlStartPosition + 1);
1215             //                        fWhiteStart = false;
1216             //                        if (startOfIndent) {
1217             //                          startOfIndent = false;
1218             //                          handleTeXMath(mathContent, false);
1219             //                        } else {
1220             //                          handleTeXMath(mathContent, true);
1221             //                        }
1222             //                        continue;
1223             //                      }
1224             //                    }
1225             //                  }
1226             //                  break;
1227             case 'n': // nowiki
1228               String nowikiString = fStringSource.substring(fCurrentPosition - 1, fCurrentPosition + 7);
1229
1230               if (nowikiString.equals("<nowiki>")) {
1231                 fCurrentPosition += 7;
1232                 if (readUntilString("</nowiki>")) {
1233                   String nowikiContent = new String(fSource, htmlStartPosition + 7, fCurrentPosition - htmlStartPosition - 16);
1234                   if (nowikiContent != null) {
1235                     copyWhite(fWhiteStart, fWhiteStartPosition, fCurrentPosition - htmlStartPosition + 1);
1236                     fWhiteStart = false;
1237                     copyNowikiNewLine(nowikiContent);
1238                     continue;
1239                   }
1240                 }
1241               }
1242               break;
1243             }
1244           } catch (IndexOutOfBoundsException e) {
1245
1246           }
1247           startOfIndent = false;
1248           fCurrentPosition = htmlStartPosition;
1249           // detect special html tags
1250           int htmlToken = getHTMLToken();
1251           if (htmlToken == WikipediaFilter.TokenIgnore) {
1252             continue;
1253             //              } else if (htmlToken > TokenIgnore) {
1254             //                return htmlToken;
1255           }
1256           fCurrentPosition = htmlStartPosition;
1257           break;
1258         case '=': // wikipedia header ?
1259           if (isStartOfLine()) {
1260             int levelHeader = getNumberOfChar('=') + 1;
1261             //            int tempPosition = checkWhitespaces(fWhiteStartPosition, fCurrentPosition - 1 - levelHeader);
1262             //            if (tempPosition >= 0) {
1263             copyWhite(fWhiteStart, fWhiteStartPosition, levelHeader);
1264             fWhiteStart = false;
1265             int startHeadPosition = fCurrentPosition;
1266             //                 int initialOffset = levelHeader;
1267             if (levelHeader > 6) {
1268               levelHeader = 6;
1269             }
1270             levelHeader--;
1271             if (readUntilString(WikipediaFilter.HEADER_STRINGS[levelHeader])) {
1272               String head = new String(fSource, startHeadPosition, fCurrentPosition - startHeadPosition - (1 + levelHeader));
1273               levelHeader++;
1274               handleHead(head, levelHeader);
1275               continue;
1276             }
1277             //            }
1278           }
1279           break;
1280         }
1281         if (!fWhiteStart) {
1282           fWhiteStart = true;
1283           fWhiteStartPosition = fCurrentPosition - 1;
1284         }
1285
1286         startOfIndent = false;
1287       }
1288       //    -----------------end switch while try--------------------
1289     } catch (IndexOutOfBoundsException e) {
1290       // end of scanner text
1291     }
1292     copyWhite(fWhiteStart, fWhiteStartPosition, 1);
1293
1294     return WikipediaFilter.TokenEOF;
1295   }
1296
1297   /**
1298    * @return
1299    */
1300   private boolean isStartOfLine() {
1301     boolean isListStart = false;
1302     if (fCurrentPosition >= 2) {
1303       char beforeChar = fSource[fCurrentPosition - 2];
1304       if (beforeChar == '\n' || beforeChar == '\r') {
1305         isListStart = true;
1306       }
1307     }
1308     if (fCurrentPosition == 1) {
1309       isListStart = true;
1310     }
1311     return isListStart;
1312   }
1313
1314   /**
1315    * @param levelStar
1316    * @param listChars
1317    *          TODO
1318    */
1319   private void appendList(char[] listChars) {
1320     int topLevel = 0;
1321     int levelStar = listChars.length;
1322     copyWhite(fWhiteStart, fWhiteStartPosition, levelStar);
1323     fWhiteStart = false;
1324     AbstractTag tok = (AbstractTag) fTokenStack.peek();
1325
1326     if (tok instanceof ListToken) {
1327       ListToken listToken = (ListToken) tok;
1328       topLevel = listToken.getLevel();
1329
1330       if (levelStar > topLevel) {
1331         while (levelStar > topLevel) {
1332           if (listChars[topLevel] == '*') {
1333             fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_UL_START, ++topLevel));
1334             fResultBuffer.append("<ul><li>");
1335           } else {
1336             fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_OL_START, ++topLevel));
1337             fResultBuffer.append("<ol><li>");
1338           }
1339         }
1340       } else if (levelStar < topLevel) {
1341         while (levelStar < topLevel) {
1342           tok = (AbstractTag) fTokenStack.peek();
1343           if (tok instanceof ListToken) {
1344             fTokenStack.pop();
1345             listToken = (ListToken) tok;
1346             if (listToken.getToken() == WikipediaFilter.TokenLIST_UL_START) {
1347               fResultBuffer.append("</li></ul></li><li>");
1348             } else {
1349               fResultBuffer.append("</li></ol></li><li>");
1350             }
1351             topLevel--;
1352           } else {
1353             break;
1354           }
1355         }
1356       } else {
1357         --topLevel;
1358         if (listToken.getToken() == WikipediaFilter.TokenLIST_UL_START && listChars[topLevel] == '#') {
1359           fTokenStack.pop();
1360           fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_OL_START, topLevel));
1361           fResultBuffer.append("</li></ul><ol><li>");
1362         } else if (listToken.getToken() == WikipediaFilter.TokenLIST_OL_START && listChars[topLevel] == '*') {
1363           fTokenStack.pop();
1364           fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_UL_START, topLevel));
1365           fResultBuffer.append("</li></ol><ul><li>");
1366         } else {
1367           fResultBuffer.append("</li><li>");
1368         }
1369       }
1370     } else {
1371       while (levelStar > topLevel) {
1372         if (listChars[topLevel] == '*') {
1373           fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_UL_START, ++topLevel));
1374           fResultBuffer.append("\n<ul><li>");
1375         } else {
1376           fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_OL_START, ++topLevel));
1377           fResultBuffer.append("\n<ol><li>");
1378         }
1379       }
1380     }
1381   }
1382
1383   private void createMacro(int startMacroPosition, String macroStartTag) {
1384     String command = "";
1385     String endTag;
1386
1387     String parameterString = null;
1388     String macroBodyString = "";
1389     int index0;
1390     int index1;
1391     if ((index0 = macroStartTag.indexOf(':')) >= 0) {
1392       command = macroStartTag.substring(0, index0);
1393       parameterString = macroStartTag.substring(index0 + 1, macroStartTag.length());
1394     } else {
1395       command = macroStartTag;
1396     }
1397     Macro macro = (Macro) fMacros.get(command);
1398
1399     String completeMacroSubString;
1400
1401     if ((macro != null) && (macro instanceof IBodyTagSupportMacro)) {
1402       endTag = '{' + command + '}';
1403       index0 = fStringSource.indexOf(endTag, fCurrentPosition);
1404
1405       if (index0 >= 0) {
1406         macroBodyString = fStringSource.substring(fCurrentPosition, index0);
1407         completeMacroSubString = new String(fSource, startMacroPosition - 1, index0 + endTag.length() - startMacroPosition + 1);
1408         fCurrentPosition = startMacroPosition + completeMacroSubString.length() - 1;
1409       } else {
1410         completeMacroSubString = new String(fSource, startMacroPosition - 1, macroStartTag.length() + 2);
1411       }
1412     } else {
1413       completeMacroSubString = new String(fSource, startMacroPosition - 1, macroStartTag.length() + 2);
1414     }
1415
1416     copyWhite(fWhiteStart, fWhiteStartPosition, 1);
1417
1418     handleMacro(completeMacroSubString, command, parameterString, macroBodyString);
1419   }
1420
1421   //  private void createExternalLink(String urlString) {
1422   //
1423   //    // Does our engine know images?
1424   //    if (fWikiEngine instanceof ImageRenderEngine) {
1425   //      fResult.append(((ImageRenderEngine) fWikiEngine).getExternalImageLink());
1426   //    }
1427   //    fResult.append("<span class=\"nobr\">");
1428   //    fResult.append("<a href=\"");
1429   //    fResult.append(Encoder.escape(urlString));
1430   //    fResult.append("\">");
1431   //    fResult.append(Encoder.toEntity(urlString.charAt(0)) +
1432   // urlString.substring(1));
1433   //    fResult.append("</a></span>");
1434   //  }
1435
1436   //  private void handleTeXMath(String mathContent, boolean inlineExpression)
1437   // {
1438   //    // TODO clean this up
1439   //    Map map = Application.get().getParameters();
1440   //    HttpServletRequest request = (HttpServletRequest) map.get("request");
1441   //    MathSniffer sniffy = MathSniffer.getBrowserDetection(request);
1442   //
1443   //    if (fCacheStaticBlockActive) {
1444   //      fCachedPage.addHTML(fResultBuffer.substring(fCacheStaticBlockStartPosition,
1445   // fResultBuffer.length()));
1446   //      fCacheStaticBlockActive = false;
1447   //    }
1448   //    // if (inlineExpression) {
1449   //    // fASMCompiler.compileMath(mathContent, "true");
1450   //    // } else {
1451   //    // fASMCompiler.compileMath(mathContent, "false");
1452   //    // }
1453   //    if (fCachedPage == WikipediaFilter.DUMMY_CACHED_PAGE) {
1454   //      switch (sniffy.getBrowserId()) {
1455   //        case MathSniffer.XMLID :
1456   //        case MathSniffer.MATHPLAYERID :
1457   //          if (inlineExpression) {
1458   //            fResultBuffer.append("<m:math><m:mstyle mathcolor=\"Black\"
1459   // displaystyle=\"true\">");
1460   //            fTeXParser.initialize(mathContent);
1461   //            fTeXParser.parse2MML(fResultBuffer);
1462   //            fResultBuffer.append("</m:mstyle></m:math>");
1463   //          } else {
1464   //            fResultBuffer.append("<dl><dd><m:math><m:mstyle mathcolor=\"Black\"
1465   // displaystyle=\"true\">");
1466   //            fTeXParser.initialize(mathContent);
1467   //            fTeXParser.parse2MML(fResultBuffer);
1468   //            fResultBuffer.append("</m:mstyle></m:math></dd></dl>");
1469   //          }
1470   //          break;
1471   //        case MathSniffer.CSSID :
1472   //          if (inlineExpression) {
1473   //            fResultBuffer.append("<m>");
1474   //            fTeXParser.initialize(mathContent);
1475   //            fTeXParser.parse2CSS(fResultBuffer, true);
1476   //            fResultBuffer.append("</m>");
1477   //          } else {
1478   //            fResultBuffer.append("<e>");
1479   //            fTeXParser.initialize(mathContent);
1480   //            fTeXParser.parse2CSS(fResultBuffer, true);
1481   //            fResultBuffer.append("</e>");
1482   //          }
1483   //          break;
1484   //        default :
1485   //          copyWhite(mathContent);
1486   //      }
1487   //    } else {
1488   //      String[] mathStrings = new String[3];
1489   //      StringBuffer mathBuffer = new StringBuffer();
1490   //      if (inlineExpression) {
1491   //        // mathml inline
1492   //        mathBuffer.append("<m:math><m:mstyle mathcolor=\"Black\"
1493   // displaystyle=\"true\">");
1494   //
1495   //        fTeXParser.initialize(mathContent);
1496   //        fTeXParser.parse2MML(mathBuffer);
1497   //        mathBuffer.append("</m:mstyle></m:math>");
1498   //      } else {
1499   //        mathBuffer.append("<dl><dd><m:math><m:mstyle mathcolor=\"Black\"
1500   // displaystyle=\"true\">");
1501   //        fTeXParser.initialize(mathContent);
1502   //        fTeXParser.parse2MML(mathBuffer);
1503   //        mathBuffer.append("</m:mstyle></m:math></dd></dl>");
1504   //      }
1505   //      mathStrings[0] = mathBuffer.toString();
1506   //      mathBuffer.setLength(0);
1507   //      // if (inlineExpression) {
1508   //      // // css inline
1509   //      // mathBuffer.append("<m>");
1510   //      // fTeXParser.initialize(mathContent);
1511   //      // fTeXParser.parse2CSS(mathBuffer, true);
1512   //      // mathBuffer.append("</m>");
1513   //      // } else {
1514   //      // // css block mode
1515   //      // mathBuffer.append("<e>");
1516   //      // fTeXParser.initialize(mathContent);
1517   //      // fTeXParser.parse2CSS(mathBuffer, true);
1518   //      // mathBuffer.append("</e>");
1519   //      // }
1520   //
1521   //      String encodedMathContent = Encoder.escape(mathContent);
1522   //      StringBuffer mathmlBuffer = new StringBuffer();
1523   //      String shortImageName = "____tex_" + (fImageCounter++);
1524   //      String longImageName = shortImageName + ".gif";
1525   //      mathmlBuffer.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
1526   //      if (inlineExpression) {
1527   //        mathmlBuffer.append("<math><mstyle mathcolor=\"Black\"
1528   // displaystyle=\"true\">");
1529   //        fTeXImageParser.initialize(mathContent);
1530   //        fTeXImageParser.parse2MML(mathmlBuffer);
1531   //        mathmlBuffer.append("</mstyle></math>");
1532   //      } else {
1533   //        mathmlBuffer.append("<math><mstyle mathcolor=\"Black\"
1534   // displaystyle=\"true\">");
1535   //        fTeXImageParser.initialize(mathContent);
1536   //        fTeXImageParser.parse2MML(mathmlBuffer);
1537   //        mathmlBuffer.append("</mstyle></math>");
1538   //      }
1539   //
1540   //      String snipname = fSnip.getName();
1541   //      String SnipSnapSpacePath =
1542   // Application.get().getConfiguration().getFilePath().getAbsolutePath();
1543   //      // Remove old image
1544   //      fSnip.getAttachments().removeAttachment(longImageName + ".gif");
1545   //
1546   //      String directoryName = SnipSnapSpacePath + "/" + snipname;
1547   //      File directory = new File(directoryName);
1548   //      if (!directory.exists()) {
1549   //        directory.mkdirs();
1550   //      }
1551   //      String gifFilename = directoryName + "/" + longImageName;
1552   //      // File file = new File();
1553   //      // Get the number of bytes in the file
1554   //      // long length = file.length();
1555   //      MathMLToGIFConverter conv = new MathMLToGIFConverter();
1556   //      // String test =
1557   //      // "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><math
1558   //      //
1559   // mode=\"display\"><mrow><munderover><mo>&#x0222B;</mo><mn>1</mn><mi>x</mi></munderover><mfrac><mi>dt</mi><mi>t</mi></mfrac></mrow></math>";
1560   //      try {
1561   //        File imageFile = conv.convert(mathmlBuffer.toString(), gifFilename);
1562   //        if (imageFile != null) {
1563   //          Attachment attachment =
1564   //            new Attachment(longImageName, "image/gif", imageFile.length(), new
1565   // Date(), snipname + "/" + longImageName, true);
1566   //          // fSnip.getAttachments().addAttachment(longImageName, "image/gif",
1567   // imageFile.length(), snipname + "/" + longImageName,
1568   //          // true);
1569   //          fSnip.getAttachments().addAttachment(attachment);
1570   //          StringWriter writer = new StringWriter();
1571   //
1572   //          SnipLink.appendImage(writer, fSnip, shortImageName, encodedMathContent,
1573   // "gif", null);
1574   //
1575   //          mathBuffer = writer.getBuffer();
1576   //          mathStrings[1] = mathBuffer.toString();
1577   //        } else {
1578   //          mathStrings[1] = encodedMathContent;
1579   //        }
1580   //      } catch (IOException e) {
1581   //        // TODO Auto-generated catch block
1582   //        e.printStackTrace();
1583   //        mathStrings[1] = encodedMathContent;
1584   //      }
1585   //      mathBuffer.setLength(0);
1586   //      WikipediaFilter.copyWhite(mathBuffer, mathContent);
1587   //      mathStrings[2] = mathBuffer.toString();
1588   //      fCachedPage.addMath(mathStrings);
1589   //    }
1590   //    if (!fCacheStaticBlockActive) {
1591   //      fCacheStaticBlockActive = true;
1592   //      fCacheStaticBlockStartPosition = fResultBuffer.length();
1593   //    }
1594   //  }
1595   private void handleSnipLink(String name) {
1596     if (name != null) {
1597       int index = name.indexOf("http://");
1598       // Configuration probably wrote [http://radeox.org] instead of
1599       // http://radeox.org
1600       if (index != -1) {
1601         // WikipediaFilter.createExternalLink(fResultBuffer,
1602         // fWikiEngine, name.substring(index));
1603         String urlString = name.substring(index);
1604         // Wikipedia like style:
1605         int pipeIndex = urlString.indexOf(' ');
1606         String alias = "";
1607         if (pipeIndex != (-1)) {
1608           alias = urlString.substring(pipeIndex + 1);
1609           urlString = urlString.substring(0, pipeIndex);
1610         } else {
1611           alias = urlString;
1612         }
1613
1614         if (fWikiEngine instanceof ImageRenderEngine) {
1615           fResultBuffer.append(((ImageRenderEngine) fWikiEngine).getExternalImageLink());
1616         }
1617         fResultBuffer.append("<span class=\"nobr\">");
1618         fResultBuffer.append("<a href=\"");
1619         fResultBuffer.append(Encoder.escape(urlString));
1620         fResultBuffer.append("\">");
1621         fResultBuffer.append(Encoder.toEntity(alias.charAt(0)) + alias.substring(1));
1622         fResultBuffer.append("</a></span>");
1623       }
1624       //      else {
1625       //        // trim the name and unescape it
1626       //        name = Encoder.unescape(name.trim());
1627       //        // Is there an alias like [alias|link] ?
1628       //        int pipeIndex = name.indexOf('|');
1629       //        String alias = "";
1630       //        if (-1 != pipeIndex) {
1631       //          alias = name.substring(0, pipeIndex);
1632       //          name = name.substring(pipeIndex + 1);
1633       //        }
1634       //
1635       //        int hashIndex = name.lastIndexOf('#');
1636       //
1637       //        String hash = "";
1638       //        if (-1 != hashIndex && hashIndex != name.length() - 1) {
1639       //          hash = name.substring(hashIndex + 1);
1640       //          name = name.substring(0, hashIndex);
1641       //        }
1642       //
1643       //        int colonIndex = name.indexOf(':');
1644       //        // typed link ?
1645       //        if (-1 != colonIndex) {
1646       //          // for now throw away the fType information
1647       //          name = name.substring(colonIndex + 1);
1648       //        }
1649       //
1650       //        int atIndex = name.lastIndexOf('@');
1651       //        // InterWiki link ?
1652       //        if (-1 != atIndex) {
1653       //          String extSpace = name.substring(atIndex + 1);
1654       //          // known extarnal space ?
1655       //          InterWiki interWiki = InterWiki.getInstance();
1656       //          if (interWiki.contains(extSpace)) {
1657       //            name = name.substring(0, atIndex);
1658       //            Writer writer = new StringBufferWriter(fResultBuffer);
1659       //            try {
1660       //              if (-1 != hashIndex) {
1661       //                interWiki.expand(writer, extSpace, name, hash);
1662       //              } else {
1663       //                interWiki.expand(writer, extSpace, name, "");
1664       //              }
1665       //            } catch (IOException e) {
1666       //              log.debug("InterWiki " + extSpace + " not found.");
1667       //            }
1668       //          } else {
1669       //            fResultBuffer.append("&#91;<span class=\"error\">");
1670       //            fResultBuffer.append(name);
1671       //            fResultBuffer.append("?</span>&#93;");
1672       //          }
1673       //        } else {
1674       //          // internal link
1675       //          if (fWikiEngine != null && fWikiEngine instanceof WikiRenderEngine) {
1676       //            if (fCacheStaticBlockActive) {
1677       //              fCachedPage.addHTML(fResultBuffer.substring(fCacheStaticBlockStartPosition, fResultBuffer.length()));
1678       //              fCacheStaticBlockActive = false;
1679       //            }
1680       //            fCachedPage.addSnipLink(name);
1681       //            if (fCachedPage == WikipediaFilter.DUMMY_CACHED_PAGE) {
1682       //              if (((WikiRenderEngine) fWikiEngine).exists(name)) {
1683       //                String view = name;
1684       //                if (-1 != pipeIndex) {
1685       //                  view = alias;
1686       //                }
1687       //                // Do not add hash if an alias was given
1688       //                if (-1 != hashIndex) {
1689       //                  ((WikiRenderEngine) fWikiEngine).appendLink(fResultBuffer, name, view, hash);
1690       //                } else {
1691       //                  ((WikiRenderEngine) fWikiEngine).appendLink(fResultBuffer, name, view);
1692       //                }
1693       //              } else if (((WikiRenderEngine) fWikiEngine).showCreate()) {
1694       //                ((WikiRenderEngine) fWikiEngine).appendCreateLink(fResultBuffer, name, name);
1695       //                // links with "create" are not cacheable because
1696       //                // a missing wiki could be created
1697       //                fContext.getRenderContext().setCacheable(false);
1698       //              } else {
1699       //                // cannot display/create wiki, so just display
1700       //                // the text
1701       //                fResultBuffer.append(name);
1702       //              }
1703       //            }
1704       //            if (!fCacheStaticBlockActive) {
1705       //              fCacheStaticBlockActive = true;
1706       //              fCacheStaticBlockStartPosition = fResultBuffer.length();
1707       //            }
1708       //          } else {
1709       //            // cannot display/create wiki, so just display the text
1710       //            fResultBuffer.append(name);
1711       //          }
1712       //        }
1713       //      }
1714     }
1715   }
1716
1717   private boolean handleWikipediaTable() {
1718     //  // example
1719     //// {| border=1
1720     //// |Zelle 1
1721     //// |
1722     //// {| border=2
1723     //// |Zelle A
1724     //// |-
1725     //// |Zelle B
1726     //// |}
1727     //// |Zelle 3
1728     //// |}
1729     int temp = fCurrentPosition;
1730     int sequenceStart = 0;
1731     char lastChar = ' ';
1732     int sequenceLength = 0;
1733     int thStartPosition = 0;
1734     Stack wpTokenStack = new Stack();
1735     try {
1736       // add table attributes:
1737       fResultBuffer.append("<table ");
1738       sequenceStart = fCurrentPosition;
1739       fCurrentCharacter = fSource[fCurrentPosition++];
1740       while (fCurrentCharacter != '\n' && fCurrentCharacter != '\r') {
1741         sequenceLength++;
1742         fCurrentCharacter = fSource[fCurrentPosition++];
1743       }
1744       if (fCurrentCharacter == '\n' || fCurrentCharacter == '\r') {
1745         fCurrentCharacter = fSource[fCurrentPosition++];
1746       }
1747       if (sequenceLength > 0) {
1748         fResultBuffer.append(fSource, sequenceStart + 1, sequenceLength - 1);
1749       }
1750       fResultBuffer.append(">");
1751       wpTokenStack.push(WikipediaFilter.HTML_TABLE_OPEN);
1752
1753       fResultBuffer.append("<tr>\n");
1754       wpTokenStack.push(WikipediaFilter.HTML_TR_OPEN);
1755       String attributes = null;
1756       // parse rest of table
1757       while (true) {
1758         sequenceStart = fCurrentPosition;
1759         fCurrentCharacter = fSource[fCurrentPosition++];
1760         if (fCurrentCharacter == '{' && fSource[fCurrentPosition] == '|') { // nested table
1761           handleWikipediaTable();
1762         } else if (fCurrentCharacter == '!') {
1763           // <th>...</th>
1764           reduceTableCellStack(fResultBuffer, wpTokenStack);
1765           fResultBuffer.append(fSource, sequenceStart, fCurrentPosition - sequenceStart - 1);
1766           sequenceStart = fCurrentPosition;
1767           // add header row cells
1768           attributes = null;
1769           while (true) {
1770             fCurrentCharacter = fSource[fCurrentPosition++];
1771             if (fCurrentCharacter == '|' || fCurrentCharacter == '\n') {
1772               if (fSource[fCurrentPosition] == '|' || fCurrentCharacter == '\n') {
1773                 reduceTableCellStack(fResultBuffer, wpTokenStack);
1774                 if (attributes == null) {
1775                   fResultBuffer.append("<th>");
1776                 } else {
1777                   fResultBuffer.append("<th ");
1778                   fResultBuffer.append(attributes);
1779                   fResultBuffer.append(">");
1780                 }
1781                 wpTokenStack.push(WikipediaFilter.HTML_TH_OPEN);
1782                 fResultBuffer.append(WikipediaFilter.filterParser(new String(fSource, sequenceStart, fCurrentPosition
1783                     - sequenceStart - 1), fContext, fMacros, fRecursionLevel));
1784                 fCurrentPosition++;
1785                 sequenceStart = fCurrentPosition;
1786                 attributes = null;
1787               } else {
1788                 attributes = new String(fSource, sequenceStart, fCurrentPosition - sequenceStart - 1);
1789                 sequenceStart = fCurrentPosition;
1790               }
1791             }
1792             if (fCurrentCharacter == '\n') {
1793               fCurrentPosition--;
1794               break;
1795             }
1796           }
1797         } else if (fCurrentCharacter == '|') {
1798           reduceTableCellStack(fResultBuffer, wpTokenStack);
1799           fResultBuffer.append(fSource, sequenceStart, fCurrentPosition - sequenceStart - 1);
1800
1801           sequenceStart = fCurrentPosition;
1802           fCurrentCharacter = fSource[fCurrentPosition++];
1803           switch (fCurrentCharacter) {
1804           case '+': // caption
1805             sequenceStart++;
1806             reduceTableRowStack(WikipediaFilter.HTML_TABLE_OPEN, fResultBuffer, wpTokenStack);
1807             fResultBuffer.append("<caption>\n");
1808             wpTokenStack.push(WikipediaFilter.HTML_CAPTION_OPEN);
1809             // read until end of line
1810             while (fCurrentCharacter != '\n' && fCurrentCharacter != '\r') {
1811               fCurrentCharacter = fSource[fCurrentPosition++];
1812             }
1813             if (fCurrentCharacter == '\n' || fCurrentCharacter == '\r') {
1814               fCurrentCharacter = fSource[fCurrentPosition++];
1815             }
1816             fResultBuffer.append(WikipediaFilter.filterParser(new String(fSource, sequenceStart, fCurrentPosition - sequenceStart
1817                 - 1), fContext, fMacros, fRecursionLevel));
1818             break;
1819           case '-': // new row
1820             sequenceStart++;
1821             reduceTableRowStack(WikipediaFilter.HTML_TR_OPEN, fResultBuffer, wpTokenStack);
1822
1823             // read until end of line
1824             while (fCurrentCharacter != '\n' && fCurrentCharacter != '\r') {
1825               fCurrentCharacter = fSource[fCurrentPosition++];
1826             }
1827             if (fCurrentCharacter == '\n' || fCurrentCharacter == '\r') {
1828               fCurrentCharacter = fSource[fCurrentPosition++];
1829             }
1830             // TODO handle row attributes
1831             fResultBuffer.append("<tr ");
1832             wpTokenStack.push(WikipediaFilter.HTML_TR_OPEN);
1833             fResultBuffer.append(fSource, sequenceStart, fCurrentPosition - sequenceStart - 1);
1834             fResultBuffer.append(">\n");
1835             //            fResultBuffer.append(WikipediaFilter.filterParser(new String(fSource, sequenceStart, fCurrentPosition
1836             //                - sequenceStart - 1), fContext, fMacros, fRecursionLevel));
1837             break;
1838           case '}': // end of table
1839             clearTableStack(fResultBuffer, wpTokenStack);
1840             //                                          System.out.println(fResultBuffer.toString());
1841             return true;
1842           default:
1843             // add row cells
1844             attributes = null;
1845             while (true) {
1846               fCurrentCharacter = fSource[fCurrentPosition++];
1847               if (fCurrentCharacter == '|' || fCurrentCharacter == '\n') {
1848                 if (fSource[fCurrentPosition] == '|' || fCurrentCharacter == '\n') {
1849                   reduceTableCellStack(fResultBuffer, wpTokenStack);
1850                   if (attributes == null) {
1851                     fResultBuffer.append("<td>");
1852                   } else {
1853                     fResultBuffer.append("<td ");
1854                     fResultBuffer.append(attributes);
1855                     fResultBuffer.append(">");
1856                   }
1857                   wpTokenStack.push(WikipediaFilter.HTML_TD_OPEN);
1858                   fResultBuffer.append(WikipediaFilter.filterParser(new String(fSource, sequenceStart, fCurrentPosition
1859                       - sequenceStart - 1), fContext, fMacros, fRecursionLevel));
1860                   // TODO reduce fTokenStack to <tr> element
1861                   // if necessary
1862                   //                  fResultBuffer.append("</td>");
1863                   //                  fTokenStack.pop();
1864                   fCurrentPosition++;
1865                   sequenceStart = fCurrentPosition;
1866                   attributes = null;
1867                 } else {
1868                   attributes = new String(fSource, sequenceStart, fCurrentPosition - sequenceStart - 1);
1869
1870                   sequenceStart = fCurrentPosition;
1871                 }
1872               }
1873               if (fCurrentCharacter == '\n') {
1874                 fCurrentPosition--;
1875                 break;
1876               }
1877             }
1878           }
1879         } else if (wpTokenStack.peek() == WikipediaFilter.HTML_TD_OPEN) {
1880           // continue a table cell in the next line
1881           while (true) {
1882             if (fCurrentCharacter == '\n') {
1883               char ch0 = fSource[fCurrentPosition];
1884               char ch1 = fSource[fCurrentPosition + 1];
1885               if (ch0 == '|' || ch0 == '!' || (ch0 == '{' && ch1 == '|')) {
1886                 fResultBuffer.append(WikipediaFilter.filterParser(new String(fSource, sequenceStart, fCurrentPosition
1887                     - sequenceStart - 1), fContext, fMacros, fRecursionLevel));
1888                 sequenceStart = fCurrentPosition;
1889                 attributes = null;
1890                 break;
1891               }
1892             }
1893             fCurrentCharacter = fSource[fCurrentPosition++];
1894           }
1895         }
1896       }
1897     } catch (IndexOutOfBoundsException e) {
1898       if (sequenceStart + 1 < fCurrentPosition && fSource[sequenceStart + 1] == '}') {
1899         //                               TODO reduce fTokenStack to <tr> element if necessary
1900         //        fResultBuffer.append("</tr></table>\n");
1901         //        wpTokenStack.pop(); // tr
1902         //        wpTokenStack.pop();// table
1903         //                              System.out.println(fResultBuffer.toString());
1904         clearTableStack(fResultBuffer, wpTokenStack);
1905         return true;
1906       }
1907     }
1908     fCurrentPosition = temp - 1;
1909     //          System.out.print(fResultBuffer.toString());
1910     return false;
1911   } //  private boolean handleWikipediaTable() {
1912
1913   private void clearTableStack(StringBuffer buffer, Stack stack) {
1914     CloseTagToken closeTag;
1915     AbstractTag tag;
1916     while (!stack.isEmpty()) {
1917       tag = (AbstractTag) stack.pop();
1918       closeTag = (CloseTagToken) WikipediaFilter.CLOSE_TAGS.get(tag.getTagName());
1919       buffer.append(closeTag.getCloseTag());
1920     }
1921   }
1922
1923   private void reduceTableRowStack(AbstractTag stopTag, StringBuffer buffer, Stack stack) {
1924     CloseTagToken closeTag;
1925     AbstractTag tag;
1926     while (!stack.isEmpty()) {
1927       tag = (AbstractTag) stack.peek();
1928       if (tag == WikipediaFilter.HTML_TABLE_OPEN) {
1929         break;
1930       }
1931       stack.pop();
1932       closeTag = (CloseTagToken) WikipediaFilter.CLOSE_TAGS.get(tag.getTagName());
1933       buffer.append(closeTag.getCloseTag());
1934       if (tag == WikipediaFilter.HTML_TR_OPEN || tag == WikipediaFilter.HTML_CAPTION_OPEN) {
1935         break;
1936       }
1937     }
1938   }
1939
1940   private void reduceTableCellStack(StringBuffer buffer, Stack stack) {
1941     CloseTagToken closeTag;
1942     AbstractTag tag;
1943     while (!stack.isEmpty()) {
1944       tag = (AbstractTag) stack.peek();
1945       if (tag != WikipediaFilter.HTML_TH_OPEN && tag != WikipediaFilter.HTML_TD_OPEN) {
1946         break;
1947       }
1948       stack.pop();
1949       closeTag = (CloseTagToken) WikipediaFilter.CLOSE_TAGS.get(tag.getTagName());
1950       buffer.append(closeTag.getCloseTag());
1951       if (tag == WikipediaFilter.HTML_TR_OPEN || tag == WikipediaFilter.HTML_CAPTION_OPEN || tag == WikipediaFilter.HTML_TH_OPEN
1952           || tag == WikipediaFilter.HTML_TD_OPEN) {
1953         break;
1954       }
1955     }
1956   }
1957
1958   //    // example
1959   //// {| border=1
1960   //// |Zelle 1
1961   //// |
1962   //// {| border=2
1963   //// |Zelle A
1964   //// |-
1965   //// |Zelle B
1966   //// |}
1967   //// |Zelle 3
1968   //// |}
1969   //
1970   //    int temp = fCurrentPosition;
1971   //    // StringBuffer suffixBuffer = new StringBuffer();
1972   //    Table table = new Table(fContext.getRenderContext());
1973   //    // char currentChar = ' ';
1974   //    char lastChar = ' ';
1975   //    // int startPosition = 0;
1976   //    // int currentPosition = 0;
1977   //    StringBuffer buffer = new StringBuffer();
1978   //    int thStartPosition = 0;
1979   //
1980   //    try { // read first line
1981   //      fCurrentCharacter = fSource[fCurrentPosition++];
1982   //      // TODO improve this for different table attributes
1983   //      while (fCurrentCharacter != '\n' ) {
1984   //        fCurrentCharacter = fSource[fCurrentPosition++];
1985   //        // System.out.println(fCurrentCharacter);
1986   //      }
1987   //      table.newRow();
1988   //      lastChar = fCurrentCharacter;
1989   //      fCurrentCharacter = fSource[fCurrentPosition++];
1990   //      while (true) {
1991   //
1992   //        switch (fCurrentCharacter) {
1993   //          // case '{' : // start of nested table ?
1994   //          // if (getNextChar('|') && handleWikipediaTable()) { // Wikipedia table
1995   // end reached
1996   //          // return true;
1997   //          // }
1998   //          // break;
1999   //          case '!' :
2000   //            if (lastChar == '\n') {
2001   //              table.addCell(buffer.toString());
2002   //              // System.out.println(buffer.toString());
2003   //              buffer.setLength(0);
2004   //              thStartPosition = fCurrentPosition;
2005   //              while (true) {
2006   //                lastChar = fCurrentCharacter;
2007   //                fCurrentCharacter = fSource[fCurrentPosition++];
2008   //                if (fCurrentCharacter == '|') {
2009   //                  break;
2010   //                }
2011   //                if (fCurrentCharacter == '\n') {
2012   //                  lastChar = '\n';
2013   //                  fCurrentPosition = thStartPosition;
2014   //                  break;
2015   //                }
2016   //              }
2017   //            } else {
2018   //              buffer.append(fCurrentCharacter);
2019   //            }
2020   //            break;
2021   //          case '|' :
2022   //            if (lastChar == '\n') {
2023   //              if (getNextChar('}')) { // Wikipedia table end reached
2024   //                table.addCell(buffer.toString());
2025   //                StringWriter writer = new StringWriter();
2026   //                try {
2027   //                  table.appendTo(writer);
2028   //                  fResultBuffer.append(writer.getBuffer());
2029   //                } catch (IOException e1) {
2030   //                  // TODO Auto-generated catch block
2031   //                  e1.printStackTrace();
2032   //                  return false;
2033   //                }
2034   //                return true;
2035   //              } else if (getNextChar('-')) {
2036   //                table.addCell(buffer.toString());
2037   //                buffer.setLength(0);
2038   //                table.newRow();
2039   //                while (true) {
2040   //                  lastChar = fCurrentCharacter;
2041   //                  fCurrentCharacter = fSource[fCurrentPosition++];
2042   //                  if (fCurrentCharacter == '|' || fCurrentCharacter == '!') {
2043   //                    break;
2044   //                  }
2045   //                }
2046   //               // continue;
2047   //              } else {
2048   //                if (buffer.length()>0) {
2049   //                  table.addCell(buffer.toString());
2050   //                  buffer.setLength(0);
2051   //                }
2052   //              }
2053   //            } else if (getNextChar('|')) {
2054   //              table.addCell(buffer.toString());
2055   //              // System.out.println(buffer.toString());
2056   //              buffer.setLength(0);
2057   //            } else {
2058   //              buffer.append(fCurrentCharacter);
2059   //            }
2060   //            break;
2061   //          default :
2062   //            buffer.append(fCurrentCharacter);
2063   //        }
2064   //        lastChar = fCurrentCharacter;
2065   //        fCurrentCharacter = fSource[fCurrentPosition++];
2066   //      }
2067   //
2068   //    } catch (IndexOutOfBoundsException e) {
2069   //
2070   //    }
2071   //    fCurrentPosition = temp - 1;
2072   //    return false;
2073   //  }
2074   private void handleWikipediaLink(String linkText, String suffix) {
2075     String name = linkText;
2076     if (name != null) {
2077       int index = name.indexOf("http://");
2078       // Configuration probably wrote [http://radeox.org] instead of
2079       // http://radeox.org
2080       if (index != -1) {
2081         WikipediaFilter.createExternalLink(fResultBuffer, fWikiEngine, name.substring(index));
2082         // show error
2083         // fResult.append("<div class=\"error\">Do not surround URLs
2084         // with [...].</div>");
2085       } else {
2086         // trim the name and unescape it
2087         name = Encoder.unescape(name.trim());
2088         //                Is there an alias like [alias|link] ?
2089         int pipeIndex = name.indexOf('|');
2090         String alias = "";
2091         if (-1 != pipeIndex) {
2092           alias = name.substring(pipeIndex + 1);
2093           name = name.substring(0, pipeIndex);
2094         }
2095
2096         int hashIndex = name.lastIndexOf('#');
2097
2098         String hash = "";
2099         if (-1 != hashIndex && hashIndex != name.length() - 1) {
2100           hash = name.substring(hashIndex + 1);
2101           name = name.substring(0, hashIndex);
2102         }
2103
2104         //                              int colonIndex = name.indexOf(':');
2105         //                              // typed link ?
2106         //                              if (-1 != colonIndex) {
2107         //                                      // for now throw away the fType information
2108         //                                      name = name.substring(colonIndex + 1);
2109         //                              }
2110
2111         int atIndex = name.lastIndexOf('@');
2112         // InterWiki link ?
2113         if (-1 != atIndex) {
2114           String extSpace = name.substring(atIndex + 1);
2115           // known extarnal space ?
2116           InterWiki interWiki = InterWiki.getInstance();
2117           if (interWiki.contains(extSpace)) {
2118             name = name.substring(0, atIndex);
2119             Writer writer = new StringBufferWriter(fResultBuffer);
2120             try {
2121               if (-1 != hashIndex) {
2122                 interWiki.expand(writer, extSpace, name, hash);
2123               } else {
2124                 interWiki.expand(writer, extSpace, name, "");
2125               }
2126             } catch (IOException e) {
2127               //              log.debug("InterWiki " + extSpace + " not found.");
2128             }
2129           } else {
2130             fResultBuffer.append("&#91;<span class=\"error\">");
2131             fResultBuffer.append(name);
2132             fResultBuffer.append("?</span>&#93;");
2133           }
2134         } else {
2135           // internal link
2136           if (name.startsWith("Image:") && (fWikiEngine instanceof ImageRenderEngine)) {
2137             // server part of rendering images
2138             ImageRenderEngine imageEngine = (ImageRenderEngine) fWikiEngine;
2139             //                  fResultBuffer.append("<img src=\"space/1/2004-11-21/5/");
2140             fResultBuffer.append("<img src=\"");
2141             fResultBuffer.append(FilterUtil.createServerImage(null, name.substring(6), null));
2142             fResultBuffer.append("\">");
2143           } else if (fWikiEngine != null && fWikiEngine instanceof WikiRenderEngine) {
2144             if (((WikiRenderEngine) fWikiEngine).exists(name)) {
2145               String view = name + suffix;
2146               if (-1 != pipeIndex) {
2147                 view = alias + suffix;
2148               }
2149               if (name.startsWith("Image:")) {
2150                 fResultBuffer.append("<img src=\"");
2151
2152                 fResultBuffer.append(FilterUtil.createHTMLLink(null, name, null));
2153                 fResultBuffer.append("\">");
2154               } else {
2155                 // Do not add hash if an alias was given
2156                 if (-1 != hashIndex) {
2157                   ((WikiRenderEngine) fWikiEngine).appendLink(fResultBuffer, name, view, hash);
2158                 } else {
2159                   ((WikiRenderEngine) fWikiEngine).appendLink(fResultBuffer, name, view);
2160                 }
2161               }
2162             } else if (((WikiRenderEngine) fWikiEngine).showCreate()) {
2163               ((WikiRenderEngine) fWikiEngine).appendCreateLink(fResultBuffer, name, name + suffix);
2164               // links with "create" are not cacheable because
2165               // a missing wiki could be created
2166               fContext.getRenderContext().setCacheable(false);
2167             } else {
2168               // cannot display/create wiki, so just display
2169               // the text
2170               fResultBuffer.append(name);
2171             }
2172           } else {
2173             // cannot display/create wiki, so just display the text
2174             fResultBuffer.append(name);
2175           }
2176         }
2177       }
2178     }
2179   }
2180
2181   public String createAnchor(String head) {
2182     StringBuffer result = new StringBuffer(head.length() + 1);
2183     char ch;
2184     result.append('a');
2185     // reduce Anchorstring
2186     for (int i = 0; i < head.length(); i++) {
2187       ch = head.charAt(i);
2188       if ('a' <= ch && 'z' >= ch) {
2189         result.append(ch);
2190       } else if ('A' <= ch && 'Z' >= ch) {
2191         result.append(ch);
2192       } else if ('0' <= ch && '9' >= ch) {
2193         result.append(ch);
2194       }
2195       //        switch (ch) {
2196       //          case ' ' :
2197       //            result.append('_');
2198       //            break;
2199       //          case '<' : // special html escape character
2200       //            fResult.append(Encoder.toEntity('<'));
2201       //            break;
2202       //          case '>' : // special html escape character
2203       //            fResult.append(Encoder.toEntity('>'));
2204       //            break;
2205       //          case '&' : // special html escape character
2206       //            fResult.append(Encoder.toEntity('&'));
2207       //            break;
2208       //          case '\'' : // special html escape character
2209       //            fResult.append(Encoder.toEntity('\''));
2210       //            break;
2211       //          case '\"' : // special html escape character
2212       //            fResult.append(Encoder.toEntity('\"'));
2213       //            break;
2214       //          default :
2215       //            result.append(ch);
2216       //        }
2217     }
2218     return result.toString();
2219   }
2220
2221   public static StringBuffer appendLink(StringBuffer buffer, String name, String view, String target) {
2222     return appendLinkWithRoot(buffer, null, name + "#" + target, view);
2223   }
2224
2225   /**
2226    * Create a link with a root and a special view. The name will not be url encoded!
2227    */
2228   public static StringBuffer appendLinkWithRoot(StringBuffer buffer, String root, String name, String view) {
2229     buffer.append("<a href=\"");
2230     if (root != null) {
2231       buffer.append(root);
2232       buffer.append("/");
2233     }
2234     buffer.append(name);
2235     buffer.append("\">");
2236     buffer.append(Encoder.escape(view));
2237     buffer.append("</a>");
2238     return buffer;
2239   }
2240
2241   /**
2242    * add an entry to the "table of content" TODO refactor this to a class
2243    * 
2244    * @param toc
2245    * @param head
2246    * @param anchor
2247    * @param headLevel
2248    */
2249   private void addToTableOfContent(ArrayList toc, String head, String anchor, int headLevel) {
2250     int level = 1;
2251     if (level == headLevel) {
2252       String snipName = "";
2253       //      if (fSnip != null) {
2254       //        snipName = fSnip.getName();
2255       //      }
2256
2257       StringBuffer link = new StringBuffer(snipName.length() + 40 + head.length() + anchor.length());
2258       link.append("<li>");
2259       //TODO create link for table of content
2260       appendLink(link, snipName, head, anchor);
2261       link.append("</li>");
2262       toc.add(link.toString());
2263     } else {
2264       if (toc.size() > 0) {
2265         if (toc.get(toc.size() - 1) instanceof ArrayList) {
2266           addToTableOfContent((ArrayList) toc.get(toc.size() - 1), head, anchor, --headLevel);
2267           return;
2268         }
2269       }
2270       ArrayList list = new ArrayList();
2271       toc.add(list);
2272       addToTableOfContent(list, head, anchor, --headLevel);
2273     }
2274   }
2275
2276   /**
2277    * handle head for table of content
2278    * 
2279    * @param head
2280    * @param headLevel
2281    */
2282   private void handleHead(String head, int headLevel) {
2283     if (head != null) {
2284       String anchor = createAnchor(head.trim());
2285
2286       if (fTableOfContent == null) {
2287         // create new table of content
2288         fTableOfContent = new ArrayList();
2289         // copy fResult and new initialization:
2290
2291         fResultBufferHeader = fResultBuffer;
2292         fResultBuffer = new StringBuffer(fResultBuffer.capacity());
2293       }
2294       addToTableOfContent(fTableOfContent, head, anchor, headLevel);
2295
2296       fResultBuffer.append("<h");
2297       fResultBuffer.append(headLevel);
2298       fResultBuffer.append("><a name=\"");
2299       fResultBuffer.append(anchor);
2300       fResultBuffer.append("\">");
2301       fResultBuffer.append(head);
2302       fResultBuffer.append("</a></h");
2303       fResultBuffer.append(headLevel);
2304       fResultBuffer.append(">");
2305       //      if (headLevel <= 2) {
2306       //        fResultBuffer.append("<hr/>");
2307       //      }
2308     }
2309   }
2310
2311   private boolean getList(char listChar, String openTag, String closeTag) {
2312     int currentPosition = fCurrentPosition;
2313     int level = getNumberOfChar(listChar) + 1;
2314     if (getNextChar('.') && getNextChar(' ')) {
2315       int tempPosition = checkWhitespaces(fWhiteStartPosition, fCurrentPosition - 3 - level);
2316       if (tempPosition >= 0) {
2317         copyWhite(fWhiteStart, fWhiteStartPosition, 2 + level);
2318         fWhiteStart = false;
2319         AbstractTag tok = (AbstractTag) fTokenStack.peek();
2320         if (tok instanceof ListToken) {
2321           ListToken listToken = (ListToken) tok;
2322           int topLevel = listToken.getLevel();
2323           if (listToken.getToken() == WikipediaFilter.TokenLIST_OL_START) {
2324             if (level > topLevel) {
2325               fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_OL_START, topLevel + 1));
2326               fResultBuffer.append(openTag + "<li>");
2327             } else if (level < topLevel) {
2328               fTokenStack.pop();
2329               fResultBuffer.append("</li>" + closeTag + "</li><li>");
2330             } else {
2331               fResultBuffer.append("</li><li>");
2332             }
2333           } else {
2334             fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_OL_START, level));
2335             fResultBuffer.append(openTag + "<li>");
2336           }
2337         } else {
2338           fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_OL_START, 1));
2339           fResultBuffer.append("\n" + openTag + "<li>");
2340         }
2341         return true;
2342       }
2343     }
2344     fCurrentPosition = currentPosition;
2345     return false;
2346   }
2347
2348   /**
2349    * read until the string is found
2350    * 
2351    * @param name
2352    * @return
2353    */
2354   private final boolean readUntilString(String testedString) {
2355     int temp = fCurrentPosition;
2356     int index = fStringSource.indexOf(testedString, fCurrentPosition);
2357     if (index != (-1)) {
2358       fCurrentPosition = index + testedString.length();
2359       return true;
2360     }
2361     return false;
2362   }
2363
2364   /**
2365    * read until character is found
2366    * 
2367    * @param name
2368    * @return
2369    */
2370   private final boolean readUntilChar(char testedChar) {
2371     int temp = fCurrentPosition;
2372     try {
2373       while ((fCurrentCharacter = fSource[fCurrentPosition++]) != testedChar) {
2374       }
2375       return true;
2376     } catch (IndexOutOfBoundsException e) {
2377       fCurrentPosition = temp;
2378       return false;
2379     }
2380   }
2381
2382   /**
2383    * read until character is found or end-of-line is reached
2384    * 
2385    * @param name
2386    * @return -1 - for IndexOutOfBoundsException; 0 - for LF found; 1 - for testedChar found
2387    */
2388   private final int readUntilCharOrEOL(char testedChar) {
2389     int temp = fCurrentPosition;
2390     try {
2391       while ((fCurrentCharacter = fSource[fCurrentPosition++]) != testedChar) {
2392         //                              if (fCurrentCharacter == '\n') {
2393         //                                      return 0;
2394         //                              }
2395       }
2396       return 1;
2397     } catch (IndexOutOfBoundsException e) {
2398       fCurrentPosition = temp;
2399       return -1;
2400     }
2401   }
2402
2403   /**
2404    * read until character is found or end-of-line is reached
2405    * 
2406    * @param name
2407    * @return -1 - for IndexOutOfBoundsException; 0 - for LF found; 1 - for testedChar found
2408    */
2409   private final boolean readUntilEOL() {
2410     int temp = fCurrentPosition;
2411     try {
2412       while (true) {
2413         fCurrentCharacter = fSource[fCurrentPosition++];
2414         if (fCurrentCharacter == '\n' || fCurrentCharacter == '\r') {
2415           return true;
2416         }
2417       }
2418     } catch (IndexOutOfBoundsException e) {
2419       --fCurrentPosition;
2420       return true;
2421     }
2422   }
2423
2424   /**
2425    * Returns the view of the wiki name that is shown to the fUser. Overwrite to support other views for example transform
2426    * "WikiLinking" to "Wiki Linking". Does nothing by default.
2427    * 
2428    * @return view The view of the wiki name
2429    */
2430   //  protected String getWikiView(String name) {
2431   //    return name;
2432   //  }
2433   private void handleMacro(String completeMacroSubString, String command, String unsplittedMacroParameters, String group3) {
2434     if (command != null) {
2435       // {$peng} are variables not macros.
2436       if (!command.startsWith("$")) {
2437         MacroParameter mParams = fContext.getMacroParameter();
2438
2439         if (group3 != null) {
2440           mParams.setContent(group3);
2441           mParams.setContentStart(0);
2442           mParams.setContentEnd(group3.length());
2443         }
2444         if (unsplittedMacroParameters != null && unsplittedMacroParameters.length() > 1) {
2445           //            mParams.setParams(parseParameters(unsplittedMacroParameters));
2446           mParams.setParams(unsplittedMacroParameters);
2447         }
2448         mParams.setStart(0);
2449         mParams.setEnd(completeMacroSubString.length());
2450
2451         // @DANGER: recursive calls may replace macros in included
2452         // source code
2453         try {
2454           if (fMacros.containsKey(command)) {
2455             Macro macro = (Macro) fMacros.get(command);
2456
2457             // recursively filter macros within macros
2458             if (null != mParams.getContent() && !(macro instanceof INoParserBodyFilterMacro)) {
2459               mParams.setContent(WikipediaFilter.filterParser(mParams.getContent(), fContext, fMacros, fRecursionLevel));
2460             }
2461             StringBufferWriter writer = new StringBufferWriter(new StringBuffer(256));
2462             macro.execute(writer, mParams);
2463             StringBuffer buffer = writer.getBuffer();
2464             if (macro instanceof IRenderResultMacro) {
2465               fResultBuffer.append(WikipediaFilter.filterParser(buffer.toString(), fContext, fMacros, fRecursionLevel));
2466             } else {
2467               fResultBuffer.append(buffer);
2468             }
2469
2470           } else if (command.startsWith("!")) {
2471
2472             RenderEngine engine = fContext.getRenderContext().getRenderEngine();
2473             if (engine instanceof IncludeRenderEngine) {
2474               String include = ((IncludeRenderEngine) engine).include(command.substring(1));
2475               if (null != include) {
2476                 // Filter paramFilter = new
2477                 // ParamFilter(mParams);
2478                 // included = paramFilter.filter(included,
2479                 // null);
2480                 //                  fResult.append(include);
2481
2482                 fResultBuffer.append(WikipediaFilter.filterParser(include, fContext, fMacros, fRecursionLevel));
2483
2484               } else {
2485                 fResultBuffer.append(command.substring(1) + " not found.");
2486               }
2487             }
2488
2489             return;
2490           } else {
2491             //              fResult.append(group0);
2492             copyWhite(completeMacroSubString);
2493             return;
2494           }
2495         } catch (IllegalArgumentException e) {
2496
2497           fResultBuffer.append("<div class=\"error\">" + command + ": " + e.getMessage() + "</div>");
2498
2499           e.printStackTrace();
2500
2501         } catch (Throwable e) {
2502           //          log.warn("MacroFilter: unable to format macro: " + command, e);
2503           fResultBuffer.append("<div class=\"error\">" + command + ": " + e.getMessage() + "</div>");
2504           e.printStackTrace();
2505           return;
2506         }
2507       } else {
2508         fResultBuffer.append("<");
2509         fResultBuffer.append(command.substring(1));
2510         fResultBuffer.append(">");
2511       }
2512     } else {
2513       //        fResult.append(group0);
2514       copyWhite(completeMacroSubString);
2515     }
2516   }
2517
2518   public void parse() {
2519     int token = WikipediaFilter.TokenSTART;
2520     fTokenStack.add(WikipediaFilter.START);
2521     //    fListStack.add(START);
2522     try {
2523       while ((token = getNextToken()) != WikipediaFilter.TokenEOF) {
2524         switch (token) {
2525         case WikipediaFilter.TokenBOLD:
2526           if (fTokenStack.peek() == WikipediaFilter.BOLD) {
2527             fTokenStack.pop();
2528             fResultBuffer.append("</b>");
2529           } else {
2530             fTokenStack.push(WikipediaFilter.BOLD);
2531             fResultBuffer.append("<b>");
2532           }
2533           break;
2534         case WikipediaFilter.TokenITALIC:
2535           if (fTokenStack.peek() == WikipediaFilter.ITALIC) {
2536             fTokenStack.pop();
2537             fResultBuffer.append("</i>");
2538           } else {
2539             fTokenStack.push(WikipediaFilter.ITALIC);
2540             fResultBuffer.append("<i>");
2541           }
2542           break;
2543         case WikipediaFilter.TokenSTRONG:
2544           if (fTokenStack.peek() == WikipediaFilter.STRONG) {
2545             fTokenStack.pop();
2546             fResultBuffer.append("</strong>");
2547           } else {
2548             fTokenStack.push(WikipediaFilter.STRONG);
2549             fResultBuffer.append("<strong>");
2550           }
2551           break;
2552         case WikipediaFilter.TokenEM:
2553           if (fTokenStack.peek() == WikipediaFilter.EM) {
2554             fTokenStack.pop();
2555             fResultBuffer.append("</em>");
2556           } else {
2557             fTokenStack.push(WikipediaFilter.EM);
2558             fResultBuffer.append("<em>");
2559           }
2560           break;
2561         case WikipediaFilter.TokenSTRIKETHROUGH:
2562           if (fTokenStack.peek() == WikipediaFilter.STRIKETHROUGH) {
2563             fTokenStack.pop();
2564             fResultBuffer.append("</strike>");
2565           } else {
2566             fTokenStack.push(WikipediaFilter.STRIKETHROUGH);
2567             fResultBuffer.append("<strike>");
2568           }
2569           break;
2570         //            case TokenLIST_UL_START :
2571         //              if (fTokenStack.peek().equals(LIST_UL_START)) {
2572         //                fResult.append("</li>\n<li>");
2573         //              } else {
2574         //                fTokenStack.push(LIST_UL_START);
2575         //                fResult.append("\n<ul class=\"star\">\n<li>");
2576         //              }
2577         //              break;
2578         //            case TokenLIST_UL_END :
2579         //              fTokenStack.pop();
2580         //              fResult.append("</li>\n</ul>\n");
2581         //              break;
2582         //            case TokenLIST_OL_START :
2583         //              if (fTokenStack.peek().equals(LIST_OL_START)) {
2584         //                fResult.append("</li>\n<li>");
2585         //              } else {
2586         //                fTokenStack.push(LIST_OL_START);
2587         //                fResult.append("\n<ol>\n<li>");
2588         //              }
2589         //              break;
2590         //            case TokenLIST_OL_END :
2591         //              fTokenStack.pop();
2592         //              fResult.append("</li>\n</ol>\n");
2593         //              break;
2594         }
2595       }
2596     } catch (InvalidInputException e) {
2597       //
2598     }
2599     // clear rest of stack if necessary (case of error in syntax!?)
2600     AbstractTag tok;
2601     while ((tok = (AbstractTag) fTokenStack.pop()) != WikipediaFilter.START) {
2602       if (tok instanceof OpenTagToken) {
2603
2604         CloseTagToken closeToken = (CloseTagToken) WikipediaFilter.CLOSE_TAGS.get(tok.getTagName());
2605         if (closeToken == null) {
2606           // here is something wrong ???
2607           fResultBuffer.append("</" + (tok.getTagName()) + ">");
2608         } else {
2609           fResultBuffer.append(closeToken.getCloseTag());
2610         }
2611       } else if (tok == WikipediaFilter.BOLD) {
2612         fResultBuffer.append("</b>");
2613       } else if (tok == WikipediaFilter.ITALIC) {
2614         fResultBuffer.append("</i>");
2615       } else if (tok == WikipediaFilter.STRONG) {
2616         fResultBuffer.append("</strong>");
2617       } else if (tok == WikipediaFilter.EM) {
2618         fResultBuffer.append("</em>");
2619       } else if (tok == WikipediaFilter.STRIKETHROUGH) {
2620         fResultBuffer.append("</strike>");
2621       } else if (tok.equals(WikipediaFilter.LIST_UL_START)) {
2622         fResultBuffer.append("</li>\n</ul>\n");
2623       } else if (tok.equals(WikipediaFilter.LIST_OL_START)) {
2624         fResultBuffer.append("</li>\n</ol>\n");
2625       }
2626     }
2627
2628     if (fResultBufferHeader != null) {
2629       int tocStart = fResultBufferHeader.length();
2630       fResultBufferHeader.append("<table id=\"toc\" border=\"0\"><tr><th>Table of contents</th></tr><tr><td>");
2631       fResultBufferHeader.append("<ol>");
2632       createToC(fTableOfContent);
2633       fResultBufferHeader.append("</ol>");
2634       fResultBufferHeader.append("</td></tr></table><hr/>");
2635
2636       fResultBufferHeader.append(fResultBuffer);
2637       fResultBuffer = fResultBufferHeader;
2638       fResultBufferHeader = null;
2639       fTableOfContent = null;
2640     }
2641   }
2642
2643   private void createToC(ArrayList toc) {
2644     if (toc.size() == 1 && (toc.get(0) instanceof ArrayList)) {
2645       createToC((ArrayList) toc.get(0));
2646       return;
2647     }
2648     for (int i = 0; i < toc.size(); i++) {
2649       if (toc.get(i) instanceof ArrayList) {
2650         fResultBufferHeader.append("<ol>");
2651         createToC((ArrayList) toc.get(i));
2652         fResultBufferHeader.append("</ol>");
2653       } else {
2654         fResultBufferHeader.append(toc.get(i));
2655       }
2656     }
2657   }
2658
2659   //  public int readUntil(String testString) throws InvalidInputException {
2660   //    startPosition = currentPosition;
2661   //    int tempPosition;
2662   //    boolean flag;
2663   //    try {
2664   //      while (true) {
2665   //        currentCharacter = source[currentPosition++];
2666   //        if (currentCharacter == testString.charAt(0)) {
2667   //          tempPosition = currentPosition;
2668   //          flag = true;
2669   //          for (int i = 1; i < testString.length(); i++) {
2670   //            currentCharacter = source[currentPosition++];
2671   //            if (currentCharacter != testString.charAt(i)) {
2672   //              flag = false;
2673   //              currentPosition = tempPosition;
2674   //              break;
2675   //            }
2676   //          }
2677   //          if (flag) {
2678   //            return TokenBODY;
2679   //          }
2680   //        }
2681   //      }
2682   //    } catch (IndexOutOfBoundsException e) {
2683   //      // end of scanner text
2684   //    }
2685   //    return TokenEOF;
2686   //  }
2687
2688   public int scanIdentifierOrKeyword(boolean isVariable) throws InvalidInputException {
2689     while (getNextCharAsWikiPluginIdentifierPart()) {
2690     }
2691     ;
2692     return WikipediaFilter.TokenIdentifier;
2693   }
2694
2695   private final void setSource(char[] source) {
2696     //the source-buffer is set to sourceString
2697     if (source == null) {
2698       this.fSource = new char[0];
2699     } else {
2700       this.fSource = source;
2701     }
2702     //      this.fEOFPosition = this.fSource.length;
2703     //      fStartPosition = -1;
2704   }
2705
2706   private void unexpectedTag(String tag) {
2707     fResultBuffer.append("<div class=\"error\">Unexpected end for tag: &lt;" + tag + "&gt;</div>");
2708   }
2709
2710   /**
2711    * @return Returns the context.
2712    */
2713   public FilterContext getContext() {
2714     return fContext;
2715   }
2716
2717   /**
2718    * @param context
2719    *          The context to set.
2720    */
2721   public void setContext(FilterContext context) {
2722     fContext = context;
2723   }
2724
2725   /**
2726    * @return Returns the wikiEngine.
2727    */
2728   public RenderEngine getWikiEngine() {
2729     return fWikiEngine;
2730   }
2731
2732   /**
2733    * @param wikiEngine
2734    *          The wikiEngine to set.
2735    */
2736   public void setWikiEngine(RenderEngine wikiEngine) {
2737     fWikiEngine = wikiEngine;
2738   }
2739 }