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