08a0e3b998dccf6e38c0c19524141071d4233cdd
[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);
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-1;
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("&nbsp;</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("&nbsp;</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 // SnipSnap / Radeox Macro Syntax
917 //            if (readUntilChar('}')) {
918 //              String macroStartTag;
919 //
920 //              macroStartTag = new String(fSource, startMacroPosition, fCurrentPosition - startMacroPosition - 1);
921 //              if (macroStartTag != null) {
922 //                createMacro(startMacroPosition, macroStartTag);
923 //                continue;
924 //              }
925 //            }
926           }
927           break;
928         case '<':
929           int htmlStartPosition = fCurrentPosition;
930           try {
931             switch (fStringSource.charAt(fCurrentPosition)) {
932             case '!': // <!-- html comment -->
933               String htmlCommentString = fStringSource.substring(fCurrentPosition - 1, fCurrentPosition + 3);
934
935               if (htmlCommentString.equals("<!--")) {
936                 fCurrentPosition += 3;
937                 if (readUntilString("-->")) {
938                   String htmlCommentContent = new String(fSource, htmlStartPosition + 3, fCurrentPosition - htmlStartPosition - 6);
939                   if (htmlCommentContent != null) {
940                     copyWhite(fWhiteStart, fWhiteStartPosition, fCurrentPosition - htmlStartPosition + 1);
941                     fWhiteStart = false;
942                     // insert html comment for visual checks
943                     // only:
944                     /*
945                      * fResultBuffer.append(" <!--"); copyWhite(htmlCommentContent); fResultBuffer.append("--> ");
946                      */
947                     continue;
948                   }
949                 }
950               }
951               break;
952             case 'n': // nowiki
953               String nowikiString = fStringSource.substring(fCurrentPosition - 1, fCurrentPosition + 7);
954
955               if (nowikiString.equals("<nowiki>")) {
956                 fCurrentPosition += 7;
957                 if (readUntilString("</nowiki>")) {
958                   String nowikiContent = new String(fSource, htmlStartPosition + 7, fCurrentPosition - htmlStartPosition - 16);
959                   if (nowikiContent != null) {
960                     copyWhite(fWhiteStart, fWhiteStartPosition, fCurrentPosition - htmlStartPosition + 1);
961                     fWhiteStart = false;
962                     copyNowikiNewLine(nowikiContent);
963                     continue;
964                   }
965                 }
966               }
967               break;
968             }
969           } catch (IndexOutOfBoundsException e) {
970             // do nothing
971           }
972           startOfIndent = false;
973           fCurrentPosition = htmlStartPosition;
974           // detect special html tags
975           int htmlToken = getHTMLToken();
976           if (htmlToken == WikipediaFilter.TokenIgnore) {
977             continue;
978             //              } else if (htmlToken > TokenIgnore) {
979             //                return htmlToken;
980           }
981           fCurrentPosition = htmlStartPosition;
982           break;
983         case '=': // wikipedia header ?
984           if (isStartOfLine()) {
985             int levelHeader = getNumberOfChar('=') + 1;
986             //            int tempPosition = checkWhitespaces(fWhiteStartPosition, fCurrentPosition - 1 - levelHeader);
987             //            if (tempPosition >= 0) {
988             copyWhite(fWhiteStart, fWhiteStartPosition, levelHeader);
989             fWhiteStart = false;
990             int startHeadPosition = fCurrentPosition;
991             //                 int initialOffset = levelHeader;
992             if (levelHeader > 6) {
993               levelHeader = 6;
994             }
995             levelHeader--;
996             if (readUntilString(WikipediaFilter.HEADER_STRINGS[levelHeader])) {
997               String head = new String(fSource, startHeadPosition, fCurrentPosition - startHeadPosition - (1 + levelHeader));
998               levelHeader++;
999               handleHead(head, levelHeader);
1000               continue;
1001             }
1002             //            }
1003           }
1004           break;
1005         }
1006         if (!fWhiteStart) {
1007           fWhiteStart = true;
1008           fWhiteStartPosition = fCurrentPosition - 1;
1009         }
1010
1011         startOfIndent = false;
1012       }
1013       //    -----------------end switch while try--------------------
1014     } catch (IndexOutOfBoundsException e) {
1015       // end of scanner text
1016     }
1017     copyWhite(fWhiteStart, fWhiteStartPosition, 1);
1018
1019     return WikipediaFilter.TokenEOF;
1020   }
1021
1022   /**
1023    * @return
1024    */
1025   private boolean isStartOfLine() {
1026     boolean isListStart = false;
1027     if (fCurrentPosition >= 2) {
1028       char beforeChar = fSource[fCurrentPosition - 2];
1029       if (beforeChar == '\n' || beforeChar == '\r') {
1030         isListStart = true;
1031       }
1032     }
1033     if (fCurrentPosition == 1) {
1034       isListStart = true;
1035     }
1036     return isListStart;
1037   }
1038
1039   /**
1040    * @param levelStar
1041    * @param listChars
1042    *          TODO
1043    */
1044   private void appendList(char[] listChars) {
1045     int topLevel = 0;
1046     int levelStar = listChars.length;
1047     copyWhite(fWhiteStart, fWhiteStartPosition, levelStar);
1048     fWhiteStart = false;
1049     AbstractTag tok = (AbstractTag) fTokenStack.peek();
1050
1051     if (tok instanceof ListToken) {
1052       ListToken listToken = (ListToken) tok;
1053       topLevel = listToken.getLevel();
1054
1055       if (levelStar > topLevel) {
1056         while (levelStar > topLevel) {
1057           if (listChars[topLevel] == '*') {
1058             fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_UL_START, ++topLevel));
1059             fResultBuffer.append("<ul><li>");
1060           } else {
1061             fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_OL_START, ++topLevel));
1062             fResultBuffer.append("<ol><li>");
1063           }
1064         }
1065       } else if (levelStar < topLevel) {
1066         while (levelStar < topLevel) {
1067           tok = (AbstractTag) fTokenStack.peek();
1068           if (tok instanceof ListToken) {
1069             fTokenStack.pop();
1070             listToken = (ListToken) tok;
1071             if (listToken.getToken() == WikipediaFilter.TokenLIST_UL_START) {
1072               fResultBuffer.append("</li></ul></li><li>");
1073             } else {
1074               fResultBuffer.append("</li></ol></li><li>");
1075             }
1076             topLevel--;
1077           } else {
1078             break;
1079           }
1080         }
1081       } else {
1082         --topLevel;
1083         if (listToken.getToken() == WikipediaFilter.TokenLIST_UL_START && listChars[topLevel] == '#') {
1084           fTokenStack.pop();
1085           fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_OL_START, topLevel));
1086           fResultBuffer.append("</li></ul><ol><li>");
1087         } else if (listToken.getToken() == WikipediaFilter.TokenLIST_OL_START && listChars[topLevel] == '*') {
1088           fTokenStack.pop();
1089           fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_UL_START, topLevel));
1090           fResultBuffer.append("</li></ol><ul><li>");
1091         } else {
1092           fResultBuffer.append("</li><li>");
1093         }
1094       }
1095     } else {
1096       while (levelStar > topLevel) {
1097         if (listChars[topLevel] == '*') {
1098           fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_UL_START, ++topLevel));
1099           fResultBuffer.append("\n<ul><li>");
1100         } else {
1101           fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_OL_START, ++topLevel));
1102           fResultBuffer.append("\n<ol><li>");
1103         }
1104       }
1105     }
1106   }
1107
1108   private void createMacro(int startMacroPosition, String macroStartTag) {
1109     String command = "";
1110     String endTag;
1111
1112     String parameterString = null;
1113     String macroBodyString = "";
1114     int index0;
1115     int index1;
1116     if ((index0 = macroStartTag.indexOf(':')) >= 0) {
1117       command = macroStartTag.substring(0, index0);
1118       parameterString = macroStartTag.substring(index0 + 1, macroStartTag.length());
1119     } else {
1120       command = macroStartTag;
1121     }
1122     Macro macro = (Macro) fMacros.get(command);
1123
1124     String completeMacroSubString;
1125
1126     if ((macro != null) && (macro instanceof IBodyTagSupportMacro)) {
1127       endTag = '{' + command + '}';
1128       index0 = fStringSource.indexOf(endTag, fCurrentPosition);
1129
1130       if (index0 >= 0) {
1131         macroBodyString = fStringSource.substring(fCurrentPosition, index0);
1132         completeMacroSubString = new String(fSource, startMacroPosition - 1, index0 + endTag.length() - startMacroPosition + 1);
1133         fCurrentPosition = startMacroPosition + completeMacroSubString.length() - 1;
1134       } else {
1135         completeMacroSubString = new String(fSource, startMacroPosition - 1, macroStartTag.length() + 2);
1136       }
1137     } else {
1138       completeMacroSubString = new String(fSource, startMacroPosition - 1, macroStartTag.length() + 2);
1139     }
1140
1141     copyWhite(fWhiteStart, fWhiteStartPosition, 1);
1142
1143     handleMacro(completeMacroSubString, command, parameterString, macroBodyString);
1144   }
1145
1146   //  private void createExternalLink(String urlString) {
1147   //
1148   //    // Does our engine know images?
1149   //    if (fWikiEngine instanceof ImageRenderEngine) {
1150   //      fResult.append(((ImageRenderEngine) fWikiEngine).getExternalImageLink());
1151   //    }
1152   //    fResult.append("<span class=\"nobr\">");
1153   //    fResult.append("<a href=\"");
1154   //    fResult.append(Encoder.escape(urlString));
1155   //    fResult.append("\">");
1156   //    fResult.append(Encoder.toEntity(urlString.charAt(0)) +
1157   // urlString.substring(1));
1158   //    fResult.append("</a></span>");
1159   //  }
1160
1161   //  private void handleTeXMath(String mathContent, boolean inlineExpression)
1162   // {
1163   //    // TODO clean this up
1164   //    Map map = Application.get().getParameters();
1165   //    HttpServletRequest request = (HttpServletRequest) map.get("request");
1166   //    MathSniffer sniffy = MathSniffer.getBrowserDetection(request);
1167   //
1168   //    if (fCacheStaticBlockActive) {
1169   //      fCachedPage.addHTML(fResultBuffer.substring(fCacheStaticBlockStartPosition,
1170   // fResultBuffer.length()));
1171   //      fCacheStaticBlockActive = false;
1172   //    }
1173   //    // if (inlineExpression) {
1174   //    // fASMCompiler.compileMath(mathContent, "true");
1175   //    // } else {
1176   //    // fASMCompiler.compileMath(mathContent, "false");
1177   //    // }
1178   //    if (fCachedPage == WikipediaFilter.DUMMY_CACHED_PAGE) {
1179   //      switch (sniffy.getBrowserId()) {
1180   //        case MathSniffer.XMLID :
1181   //        case MathSniffer.MATHPLAYERID :
1182   //          if (inlineExpression) {
1183   //            fResultBuffer.append("<m:math><m:mstyle mathcolor=\"Black\"
1184   // displaystyle=\"true\">");
1185   //            fTeXParser.initialize(mathContent);
1186   //            fTeXParser.parse2MML(fResultBuffer);
1187   //            fResultBuffer.append("</m:mstyle></m:math>");
1188   //          } else {
1189   //            fResultBuffer.append("<dl><dd><m:math><m:mstyle mathcolor=\"Black\"
1190   // displaystyle=\"true\">");
1191   //            fTeXParser.initialize(mathContent);
1192   //            fTeXParser.parse2MML(fResultBuffer);
1193   //            fResultBuffer.append("</m:mstyle></m:math></dd></dl>");
1194   //          }
1195   //          break;
1196   //        case MathSniffer.CSSID :
1197   //          if (inlineExpression) {
1198   //            fResultBuffer.append("<m>");
1199   //            fTeXParser.initialize(mathContent);
1200   //            fTeXParser.parse2CSS(fResultBuffer, true);
1201   //            fResultBuffer.append("</m>");
1202   //          } else {
1203   //            fResultBuffer.append("<e>");
1204   //            fTeXParser.initialize(mathContent);
1205   //            fTeXParser.parse2CSS(fResultBuffer, true);
1206   //            fResultBuffer.append("</e>");
1207   //          }
1208   //          break;
1209   //        default :
1210   //          copyWhite(mathContent);
1211   //      }
1212   //    } else {
1213   //      String[] mathStrings = new String[3];
1214   //      StringBuffer mathBuffer = new StringBuffer();
1215   //      if (inlineExpression) {
1216   //        // mathml inline
1217   //        mathBuffer.append("<m:math><m:mstyle mathcolor=\"Black\"
1218   // displaystyle=\"true\">");
1219   //
1220   //        fTeXParser.initialize(mathContent);
1221   //        fTeXParser.parse2MML(mathBuffer);
1222   //        mathBuffer.append("</m:mstyle></m:math>");
1223   //      } else {
1224   //        mathBuffer.append("<dl><dd><m:math><m:mstyle mathcolor=\"Black\"
1225   // displaystyle=\"true\">");
1226   //        fTeXParser.initialize(mathContent);
1227   //        fTeXParser.parse2MML(mathBuffer);
1228   //        mathBuffer.append("</m:mstyle></m:math></dd></dl>");
1229   //      }
1230   //      mathStrings[0] = mathBuffer.toString();
1231   //      mathBuffer.setLength(0);
1232   //      // if (inlineExpression) {
1233   //      // // css inline
1234   //      // mathBuffer.append("<m>");
1235   //      // fTeXParser.initialize(mathContent);
1236   //      // fTeXParser.parse2CSS(mathBuffer, true);
1237   //      // mathBuffer.append("</m>");
1238   //      // } else {
1239   //      // // css block mode
1240   //      // mathBuffer.append("<e>");
1241   //      // fTeXParser.initialize(mathContent);
1242   //      // fTeXParser.parse2CSS(mathBuffer, true);
1243   //      // mathBuffer.append("</e>");
1244   //      // }
1245   //
1246   //      String encodedMathContent = Encoder.escape(mathContent);
1247   //      StringBuffer mathmlBuffer = new StringBuffer();
1248   //      String shortImageName = "____tex_" + (fImageCounter++);
1249   //      String longImageName = shortImageName + ".gif";
1250   //      mathmlBuffer.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
1251   //      if (inlineExpression) {
1252   //        mathmlBuffer.append("<math><mstyle mathcolor=\"Black\"
1253   // displaystyle=\"true\">");
1254   //        fTeXImageParser.initialize(mathContent);
1255   //        fTeXImageParser.parse2MML(mathmlBuffer);
1256   //        mathmlBuffer.append("</mstyle></math>");
1257   //      } else {
1258   //        mathmlBuffer.append("<math><mstyle mathcolor=\"Black\"
1259   // displaystyle=\"true\">");
1260   //        fTeXImageParser.initialize(mathContent);
1261   //        fTeXImageParser.parse2MML(mathmlBuffer);
1262   //        mathmlBuffer.append("</mstyle></math>");
1263   //      }
1264   //
1265   //      String snipname = fSnip.getName();
1266   //      String SnipSnapSpacePath =
1267   // Application.get().getConfiguration().getFilePath().getAbsolutePath();
1268   //      // Remove old image
1269   //      fSnip.getAttachments().removeAttachment(longImageName + ".gif");
1270   //
1271   //      String directoryName = SnipSnapSpacePath + "/" + snipname;
1272   //      File directory = new File(directoryName);
1273   //      if (!directory.exists()) {
1274   //        directory.mkdirs();
1275   //      }
1276   //      String gifFilename = directoryName + "/" + longImageName;
1277   //      // File file = new File();
1278   //      // Get the number of bytes in the file
1279   //      // long length = file.length();
1280   //      MathMLToGIFConverter conv = new MathMLToGIFConverter();
1281   //      // String test =
1282   //      // "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><math
1283   //      //
1284   // mode=\"display\"><mrow><munderover><mo>&#x0222B;</mo><mn>1</mn><mi>x</mi></munderover><mfrac><mi>dt</mi><mi>t</mi></mfrac></mrow></math>";
1285   //      try {
1286   //        File imageFile = conv.convert(mathmlBuffer.toString(), gifFilename);
1287   //        if (imageFile != null) {
1288   //          Attachment attachment =
1289   //            new Attachment(longImageName, "image/gif", imageFile.length(), new
1290   // Date(), snipname + "/" + longImageName, true);
1291   //          // fSnip.getAttachments().addAttachment(longImageName, "image/gif",
1292   // imageFile.length(), snipname + "/" + longImageName,
1293   //          // true);
1294   //          fSnip.getAttachments().addAttachment(attachment);
1295   //          StringWriter writer = new StringWriter();
1296   //
1297   //          SnipLink.appendImage(writer, fSnip, shortImageName, encodedMathContent,
1298   // "gif", null);
1299   //
1300   //          mathBuffer = writer.getBuffer();
1301   //          mathStrings[1] = mathBuffer.toString();
1302   //        } else {
1303   //          mathStrings[1] = encodedMathContent;
1304   //        }
1305   //      } catch (IOException e) {
1306   //        // TODO Auto-generated catch block
1307   //        e.printStackTrace();
1308   //        mathStrings[1] = encodedMathContent;
1309   //      }
1310   //      mathBuffer.setLength(0);
1311   //      WikipediaFilter.copyWhite(mathBuffer, mathContent);
1312   //      mathStrings[2] = mathBuffer.toString();
1313   //      fCachedPage.addMath(mathStrings);
1314   //    }
1315   //    if (!fCacheStaticBlockActive) {
1316   //      fCacheStaticBlockActive = true;
1317   //      fCacheStaticBlockStartPosition = fResultBuffer.length();
1318   //    }
1319   //  }
1320   private void handleSnipLink(String name) {
1321     if (name != null) {
1322       int index = name.indexOf("http://");
1323       // Configuration probably wrote [http://radeox.org] instead of
1324       // http://radeox.org
1325       if (index != -1) {
1326         // WikipediaFilter.createExternalLink(fResultBuffer,
1327         // fWikiEngine, name.substring(index));
1328         String urlString = name.substring(index);
1329         // Wikipedia like style:
1330         int pipeIndex = urlString.indexOf(' ');
1331         String alias = "";
1332         if (pipeIndex != (-1)) {
1333           alias = urlString.substring(pipeIndex + 1);
1334           urlString = urlString.substring(0, pipeIndex);
1335         } else {
1336           alias = urlString;
1337         }
1338
1339         if (fWikiEngine instanceof ImageRenderEngine) {
1340           fResultBuffer.append(((ImageRenderEngine) fWikiEngine).getExternalImageLink());
1341         }
1342         fResultBuffer.append("<span class=\"nobr\">");
1343         fResultBuffer.append("<a href=\"");
1344         fResultBuffer.append(Encoder.escape(urlString));
1345         fResultBuffer.append("\">");
1346         fResultBuffer.append(Encoder.toEntity(alias.charAt(0)) + alias.substring(1));
1347         fResultBuffer.append("</a></span>");
1348       }
1349       //      else {
1350       //        // trim the name and unescape it
1351       //        name = Encoder.unescape(name.trim());
1352       //        // Is there an alias like [alias|link] ?
1353       //        int pipeIndex = name.indexOf('|');
1354       //        String alias = "";
1355       //        if (-1 != pipeIndex) {
1356       //          alias = name.substring(0, pipeIndex);
1357       //          name = name.substring(pipeIndex + 1);
1358       //        }
1359       //
1360       //        int hashIndex = name.lastIndexOf('#');
1361       //
1362       //        String hash = "";
1363       //        if (-1 != hashIndex && hashIndex != name.length() - 1) {
1364       //          hash = name.substring(hashIndex + 1);
1365       //          name = name.substring(0, hashIndex);
1366       //        }
1367       //
1368       //        int colonIndex = name.indexOf(':');
1369       //        // typed link ?
1370       //        if (-1 != colonIndex) {
1371       //          // for now throw away the fType information
1372       //          name = name.substring(colonIndex + 1);
1373       //        }
1374       //
1375       //        int atIndex = name.lastIndexOf('@');
1376       //        // InterWiki link ?
1377       //        if (-1 != atIndex) {
1378       //          String extSpace = name.substring(atIndex + 1);
1379       //          // known extarnal space ?
1380       //          InterWiki interWiki = InterWiki.getInstance();
1381       //          if (interWiki.contains(extSpace)) {
1382       //            name = name.substring(0, atIndex);
1383       //            Writer writer = new StringBufferWriter(fResultBuffer);
1384       //            try {
1385       //              if (-1 != hashIndex) {
1386       //                interWiki.expand(writer, extSpace, name, hash);
1387       //              } else {
1388       //                interWiki.expand(writer, extSpace, name, "");
1389       //              }
1390       //            } catch (IOException e) {
1391       //              log.debug("InterWiki " + extSpace + " not found.");
1392       //            }
1393       //          } else {
1394       //            fResultBuffer.append("&#91;<span class=\"error\">");
1395       //            fResultBuffer.append(name);
1396       //            fResultBuffer.append("?</span>&#93;");
1397       //          }
1398       //        } else {
1399       //          // internal link
1400       //          if (fWikiEngine != null && fWikiEngine instanceof WikiRenderEngine) {
1401       //            if (fCacheStaticBlockActive) {
1402       //              fCachedPage.addHTML(fResultBuffer.substring(fCacheStaticBlockStartPosition, fResultBuffer.length()));
1403       //              fCacheStaticBlockActive = false;
1404       //            }
1405       //            fCachedPage.addSnipLink(name);
1406       //            if (fCachedPage == WikipediaFilter.DUMMY_CACHED_PAGE) {
1407       //              if (((WikiRenderEngine) fWikiEngine).exists(name)) {
1408       //                String view = name;
1409       //                if (-1 != pipeIndex) {
1410       //                  view = alias;
1411       //                }
1412       //                // Do not add hash if an alias was given
1413       //                if (-1 != hashIndex) {
1414       //                  ((WikiRenderEngine) fWikiEngine).appendLink(fResultBuffer, name, view, hash);
1415       //                } else {
1416       //                  ((WikiRenderEngine) fWikiEngine).appendLink(fResultBuffer, name, view);
1417       //                }
1418       //              } else if (((WikiRenderEngine) fWikiEngine).showCreate()) {
1419       //                ((WikiRenderEngine) fWikiEngine).appendCreateLink(fResultBuffer, name, name);
1420       //                // links with "create" are not cacheable because
1421       //                // a missing wiki could be created
1422       //                fContext.getRenderContext().setCacheable(false);
1423       //              } else {
1424       //                // cannot display/create wiki, so just display
1425       //                // the text
1426       //                fResultBuffer.append(name);
1427       //              }
1428       //            }
1429       //            if (!fCacheStaticBlockActive) {
1430       //              fCacheStaticBlockActive = true;
1431       //              fCacheStaticBlockStartPosition = fResultBuffer.length();
1432       //            }
1433       //          } else {
1434       //            // cannot display/create wiki, so just display the text
1435       //            fResultBuffer.append(name);
1436       //          }
1437       //        }
1438       //      }
1439     }
1440   }
1441
1442   private boolean handleWikipediaTable() {
1443     //  // example
1444     //// {| border=1
1445     //// |Zelle 1
1446     //// |
1447     //// {| border=2
1448     //// |Zelle A
1449     //// |-
1450     //// |Zelle B
1451     //// |}
1452     //// |Zelle 3
1453     //// |}
1454     int temp = fCurrentPosition;
1455     int sequenceStart = 0;
1456     char lastChar = ' ';
1457     int sequenceLength = 0;
1458     int thStartPosition = 0;
1459     Stack wpTokenStack = new Stack();
1460     try {
1461       // add table attributes:
1462       fResultBuffer.append("<table ");
1463       sequenceStart = fCurrentPosition;
1464       fCurrentCharacter = fSource[fCurrentPosition++];
1465       while (fCurrentCharacter != '\n' && fCurrentCharacter != '\r') {
1466         sequenceLength++;
1467         fCurrentCharacter = fSource[fCurrentPosition++];
1468       }
1469       if (fCurrentCharacter == '\n' || fCurrentCharacter == '\r') {
1470         fCurrentCharacter = fSource[fCurrentPosition++];
1471       }
1472       if (sequenceLength > 0) {
1473         fResultBuffer.append(fSource, sequenceStart + 1, sequenceLength - 1);
1474       }
1475       fResultBuffer.append(">");
1476       wpTokenStack.push(WikipediaFilter.HTML_TABLE_OPEN);
1477
1478       fResultBuffer.append("<tr>\n");
1479       wpTokenStack.push(WikipediaFilter.HTML_TR_OPEN);
1480       String attributes = null;
1481       // parse rest of table
1482       while (true) {
1483         sequenceStart = fCurrentPosition;
1484         fCurrentCharacter = fSource[fCurrentPosition++];
1485         if (fCurrentCharacter == '{' && fSource[fCurrentPosition] == '|') { // nested table
1486           handleWikipediaTable();
1487         } else if (fCurrentCharacter == '!') {
1488           // <th>...</th>
1489           reduceTableCellStack(fResultBuffer, wpTokenStack);
1490           fResultBuffer.append(fSource, sequenceStart, fCurrentPosition - sequenceStart - 1);
1491           sequenceStart = fCurrentPosition;
1492           // add header row cells
1493           attributes = null;
1494           while (true) {
1495             fCurrentCharacter = fSource[fCurrentPosition++];
1496             if (fCurrentCharacter == '|' || fCurrentCharacter == '\n') {
1497               if (fSource[fCurrentPosition] == '|' || fCurrentCharacter == '\n') {
1498                 reduceTableCellStack(fResultBuffer, wpTokenStack);
1499                 if (attributes == null) {
1500                   fResultBuffer.append("<th>");
1501                 } else {
1502                   fResultBuffer.append("<th ");
1503                   fResultBuffer.append(attributes);
1504                   fResultBuffer.append(">");
1505                 }
1506                 wpTokenStack.push(WikipediaFilter.HTML_TH_OPEN);
1507                 fResultBuffer.append(WikipediaFilter.filterParser(new String(fSource, sequenceStart, fCurrentPosition
1508                     - sequenceStart - 1), fContext, fMacros, fRecursionLevel));
1509                 fCurrentPosition++;
1510                 sequenceStart = fCurrentPosition;
1511                 attributes = null;
1512               } else {
1513                 attributes = new String(fSource, sequenceStart, fCurrentPosition - sequenceStart - 1);
1514                 sequenceStart = fCurrentPosition;
1515               }
1516             }
1517             if (fCurrentCharacter == '\n') {
1518               fCurrentPosition--;
1519               break;
1520             }
1521           }
1522         } else if (fCurrentCharacter == '|') {
1523           reduceTableCellStack(fResultBuffer, wpTokenStack);
1524           fResultBuffer.append(fSource, sequenceStart, fCurrentPosition - sequenceStart - 1);
1525
1526           sequenceStart = fCurrentPosition;
1527           fCurrentCharacter = fSource[fCurrentPosition++];
1528           switch (fCurrentCharacter) {
1529           case '+': // caption
1530             sequenceStart++;
1531             reduceTableRowStack(WikipediaFilter.HTML_TABLE_OPEN, fResultBuffer, wpTokenStack);
1532             fResultBuffer.append("<caption>\n");
1533             wpTokenStack.push(WikipediaFilter.HTML_CAPTION_OPEN);
1534             // read until end of line
1535             while (fCurrentCharacter != '\n' && fCurrentCharacter != '\r') {
1536               fCurrentCharacter = fSource[fCurrentPosition++];
1537             }
1538             if (fCurrentCharacter == '\n' || fCurrentCharacter == '\r') {
1539               fCurrentCharacter = fSource[fCurrentPosition++];
1540             }
1541             fResultBuffer.append(WikipediaFilter.filterParser(new String(fSource, sequenceStart, fCurrentPosition - sequenceStart
1542                 - 1), fContext, fMacros, fRecursionLevel));
1543             break;
1544           case '-': // new row
1545             sequenceStart++;
1546             reduceTableRowStack(WikipediaFilter.HTML_TR_OPEN, fResultBuffer, wpTokenStack);
1547
1548             // read until end of line
1549             while (fCurrentCharacter != '\n' && fCurrentCharacter != '\r') {
1550               fCurrentCharacter = fSource[fCurrentPosition++];
1551             }
1552             if (fCurrentCharacter == '\n' || fCurrentCharacter == '\r') {
1553               fCurrentCharacter = fSource[fCurrentPosition++];
1554             }
1555             // TODO handle row attributes
1556             fResultBuffer.append("<tr ");
1557             wpTokenStack.push(WikipediaFilter.HTML_TR_OPEN);
1558             fResultBuffer.append(fSource, sequenceStart, fCurrentPosition - sequenceStart - 1);
1559             fResultBuffer.append(">\n");
1560             //            fResultBuffer.append(WikipediaFilter.filterParser(new String(fSource, sequenceStart, fCurrentPosition
1561             //                - sequenceStart - 1), fContext, fMacros, fRecursionLevel));
1562             break;
1563           case '}': // end of table
1564             clearTableStack(fResultBuffer, wpTokenStack);
1565             //                                          System.out.println(fResultBuffer.toString());
1566             return true;
1567           default:
1568             // add row cells
1569             attributes = null;
1570             while (true) {
1571               fCurrentCharacter = fSource[fCurrentPosition++];
1572               if (fCurrentCharacter == '|' || fCurrentCharacter == '\n') {
1573                 if (fSource[fCurrentPosition] == '|' || fCurrentCharacter == '\n') {
1574                   reduceTableCellStack(fResultBuffer, wpTokenStack);
1575                   if (attributes == null) {
1576                     fResultBuffer.append("<td>");
1577                   } else {
1578                     fResultBuffer.append("<td ");
1579                     fResultBuffer.append(attributes);
1580                     fResultBuffer.append(">");
1581                   }
1582                   wpTokenStack.push(WikipediaFilter.HTML_TD_OPEN);
1583                   fResultBuffer.append(WikipediaFilter.filterParser(new String(fSource, sequenceStart, fCurrentPosition
1584                       - sequenceStart - 1), fContext, fMacros, fRecursionLevel));
1585                   // TODO reduce fTokenStack to <tr> element
1586                   // if necessary
1587                   //                  fResultBuffer.append("</td>");
1588                   //                  fTokenStack.pop();
1589                   fCurrentPosition++;
1590                   sequenceStart = fCurrentPosition;
1591                   attributes = null;
1592                 } else {
1593                   attributes = new String(fSource, sequenceStart, fCurrentPosition - sequenceStart - 1);
1594
1595                   sequenceStart = fCurrentPosition;
1596                 }
1597               }
1598               if (fCurrentCharacter == '\n') {
1599                 fCurrentPosition--;
1600                 break;
1601               }
1602             }
1603           }
1604         } else if (wpTokenStack.peek() == WikipediaFilter.HTML_TD_OPEN) {
1605           // continue a table cell in the next line
1606           while (true) {
1607             if (fCurrentCharacter == '\n') {
1608               char ch0 = fSource[fCurrentPosition];
1609               char ch1 = fSource[fCurrentPosition + 1];
1610               if (ch0 == '|' || ch0 == '!' || (ch0 == '{' && ch1 == '|')) {
1611                 fResultBuffer.append(WikipediaFilter.filterParser(new String(fSource, sequenceStart, fCurrentPosition
1612                     - sequenceStart - 1), fContext, fMacros, fRecursionLevel));
1613                 sequenceStart = fCurrentPosition;
1614                 attributes = null;
1615                 break;
1616               }
1617             }
1618             fCurrentCharacter = fSource[fCurrentPosition++];
1619           }
1620         }
1621       }
1622     } catch (IndexOutOfBoundsException e) {
1623       if (sequenceStart + 1 < fCurrentPosition && fSource[sequenceStart + 1] == '}') {
1624         //                               TODO reduce fTokenStack to <tr> element if necessary
1625         //        fResultBuffer.append("</tr></table>\n");
1626         //        wpTokenStack.pop(); // tr
1627         //        wpTokenStack.pop();// table
1628         //                              System.out.println(fResultBuffer.toString());
1629         clearTableStack(fResultBuffer, wpTokenStack);
1630         return true;
1631       }
1632     }
1633     fCurrentPosition = temp - 1;
1634     //          System.out.print(fResultBuffer.toString());
1635     return false;
1636   } //  private boolean handleWikipediaTable() {
1637
1638   private void clearTableStack(StringBuffer buffer, Stack stack) {
1639     CloseTagToken closeTag;
1640     AbstractTag tag;
1641     while (!stack.isEmpty()) {
1642       tag = (AbstractTag) stack.pop();
1643       closeTag = (CloseTagToken) WikipediaFilter.CLOSE_TAGS.get(tag.getTagName());
1644       buffer.append(closeTag.getCloseTag());
1645     }
1646   }
1647
1648   private void reduceTableRowStack(AbstractTag stopTag, StringBuffer buffer, Stack stack) {
1649     CloseTagToken closeTag;
1650     AbstractTag tag;
1651     while (!stack.isEmpty()) {
1652       tag = (AbstractTag) stack.peek();
1653       if (tag == WikipediaFilter.HTML_TABLE_OPEN) {
1654         break;
1655       }
1656       stack.pop();
1657       closeTag = (CloseTagToken) WikipediaFilter.CLOSE_TAGS.get(tag.getTagName());
1658       buffer.append(closeTag.getCloseTag());
1659       if (tag == WikipediaFilter.HTML_TR_OPEN || tag == WikipediaFilter.HTML_CAPTION_OPEN) {
1660         break;
1661       }
1662     }
1663   }
1664
1665   private void reduceTableCellStack(StringBuffer buffer, Stack stack) {
1666     CloseTagToken closeTag;
1667     AbstractTag tag;
1668     while (!stack.isEmpty()) {
1669       tag = (AbstractTag) stack.peek();
1670       if (tag != WikipediaFilter.HTML_TH_OPEN && tag != WikipediaFilter.HTML_TD_OPEN) {
1671         break;
1672       }
1673       stack.pop();
1674       closeTag = (CloseTagToken) WikipediaFilter.CLOSE_TAGS.get(tag.getTagName());
1675       buffer.append(closeTag.getCloseTag());
1676       if (tag == WikipediaFilter.HTML_TR_OPEN || tag == WikipediaFilter.HTML_CAPTION_OPEN || tag == WikipediaFilter.HTML_TH_OPEN
1677           || tag == WikipediaFilter.HTML_TD_OPEN) {
1678         break;
1679       }
1680     }
1681   }
1682
1683   //    // example
1684   //// {| border=1
1685   //// |Zelle 1
1686   //// |
1687   //// {| border=2
1688   //// |Zelle A
1689   //// |-
1690   //// |Zelle B
1691   //// |}
1692   //// |Zelle 3
1693   //// |}
1694   //
1695   //    int temp = fCurrentPosition;
1696   //    // StringBuffer suffixBuffer = new StringBuffer();
1697   //    Table table = new Table(fContext.getRenderContext());
1698   //    // char currentChar = ' ';
1699   //    char lastChar = ' ';
1700   //    // int startPosition = 0;
1701   //    // int currentPosition = 0;
1702   //    StringBuffer buffer = new StringBuffer();
1703   //    int thStartPosition = 0;
1704   //
1705   //    try { // read first line
1706   //      fCurrentCharacter = fSource[fCurrentPosition++];
1707   //      // TODO improve this for different table attributes
1708   //      while (fCurrentCharacter != '\n' ) {
1709   //        fCurrentCharacter = fSource[fCurrentPosition++];
1710   //        // System.out.println(fCurrentCharacter);
1711   //      }
1712   //      table.newRow();
1713   //      lastChar = fCurrentCharacter;
1714   //      fCurrentCharacter = fSource[fCurrentPosition++];
1715   //      while (true) {
1716   //
1717   //        switch (fCurrentCharacter) {
1718   //          // case '{' : // start of nested table ?
1719   //          // if (getNextChar('|') && handleWikipediaTable()) { // Wikipedia table
1720   // end reached
1721   //          // return true;
1722   //          // }
1723   //          // break;
1724   //          case '!' :
1725   //            if (lastChar == '\n') {
1726   //              table.addCell(buffer.toString());
1727   //              // System.out.println(buffer.toString());
1728   //              buffer.setLength(0);
1729   //              thStartPosition = fCurrentPosition;
1730   //              while (true) {
1731   //                lastChar = fCurrentCharacter;
1732   //                fCurrentCharacter = fSource[fCurrentPosition++];
1733   //                if (fCurrentCharacter == '|') {
1734   //                  break;
1735   //                }
1736   //                if (fCurrentCharacter == '\n') {
1737   //                  lastChar = '\n';
1738   //                  fCurrentPosition = thStartPosition;
1739   //                  break;
1740   //                }
1741   //              }
1742   //            } else {
1743   //              buffer.append(fCurrentCharacter);
1744   //            }
1745   //            break;
1746   //          case '|' :
1747   //            if (lastChar == '\n') {
1748   //              if (getNextChar('}')) { // Wikipedia table end reached
1749   //                table.addCell(buffer.toString());
1750   //                StringWriter writer = new StringWriter();
1751   //                try {
1752   //                  table.appendTo(writer);
1753   //                  fResultBuffer.append(writer.getBuffer());
1754   //                } catch (IOException e1) {
1755   //                  // TODO Auto-generated catch block
1756   //                  e1.printStackTrace();
1757   //                  return false;
1758   //                }
1759   //                return true;
1760   //              } else if (getNextChar('-')) {
1761   //                table.addCell(buffer.toString());
1762   //                buffer.setLength(0);
1763   //                table.newRow();
1764   //                while (true) {
1765   //                  lastChar = fCurrentCharacter;
1766   //                  fCurrentCharacter = fSource[fCurrentPosition++];
1767   //                  if (fCurrentCharacter == '|' || fCurrentCharacter == '!') {
1768   //                    break;
1769   //                  }
1770   //                }
1771   //               // continue;
1772   //              } else {
1773   //                if (buffer.length()>0) {
1774   //                  table.addCell(buffer.toString());
1775   //                  buffer.setLength(0);
1776   //                }
1777   //              }
1778   //            } else if (getNextChar('|')) {
1779   //              table.addCell(buffer.toString());
1780   //              // System.out.println(buffer.toString());
1781   //              buffer.setLength(0);
1782   //            } else {
1783   //              buffer.append(fCurrentCharacter);
1784   //            }
1785   //            break;
1786   //          default :
1787   //            buffer.append(fCurrentCharacter);
1788   //        }
1789   //        lastChar = fCurrentCharacter;
1790   //        fCurrentCharacter = fSource[fCurrentPosition++];
1791   //      }
1792   //
1793   //    } catch (IndexOutOfBoundsException e) {
1794   //
1795   //    }
1796   //    fCurrentPosition = temp - 1;
1797   //    return false;
1798   //  }
1799   private void handleWikipediaLink(String linkText, String suffix) {
1800     String name = linkText;
1801     if (name != null) {
1802       int index = name.indexOf("http://");
1803       // Configuration probably wrote [http://radeox.org] instead of
1804       // http://radeox.org
1805       if (index != -1) {
1806         WikipediaFilter.createExternalLink(fResultBuffer, fWikiEngine, name.substring(index));
1807         // show error
1808         // fResult.append("<div class=\"error\">Do not surround URLs
1809         // with [...].</div>");
1810       } else {
1811         // trim the name and unescape it
1812         name = Encoder.unescape(name.trim());
1813         //                Is there an alias like [alias|link] ?
1814         int pipeIndex = name.indexOf('|');
1815         String alias = "";
1816         if (-1 != pipeIndex) {
1817           alias = name.substring(pipeIndex + 1);
1818           name = name.substring(0, pipeIndex);
1819         }
1820
1821         int hashIndex = name.lastIndexOf('#');
1822
1823         String hash = "";
1824         if (-1 != hashIndex && hashIndex != name.length() - 1) {
1825           hash = name.substring(hashIndex + 1);
1826           name = name.substring(0, hashIndex);
1827         }
1828
1829         //                              int colonIndex = name.indexOf(':');
1830         //                              // typed link ?
1831         //                              if (-1 != colonIndex) {
1832         //                                      // for now throw away the fType information
1833         //                                      name = name.substring(colonIndex + 1);
1834         //                              }
1835
1836         int atIndex = name.lastIndexOf('@');
1837         // InterWiki link ?
1838         if (-1 != atIndex) {
1839           String extSpace = name.substring(atIndex + 1);
1840           // known extarnal space ?
1841           InterWiki interWiki = InterWiki.getInstance();
1842           if (interWiki.contains(extSpace)) {
1843             name = name.substring(0, atIndex);
1844             Writer writer = new StringBufferWriter(fResultBuffer);
1845             try {
1846               if (-1 != hashIndex) {
1847                 interWiki.expand(writer, extSpace, name, hash);
1848               } else {
1849                 interWiki.expand(writer, extSpace, name, "");
1850               }
1851             } catch (IOException e) {
1852               //              log.debug("InterWiki " + extSpace + " not found.");
1853             }
1854           } else {
1855             fResultBuffer.append("&#91;<span class=\"error\">");
1856             fResultBuffer.append(name);
1857             fResultBuffer.append("?</span>&#93;");
1858           }
1859         } else {
1860           // internal link
1861           if (name.startsWith("Image:") && (fWikiEngine instanceof ImageRenderEngine)) {
1862             // server part of rendering images
1863             ImageRenderEngine imageEngine = (ImageRenderEngine) fWikiEngine;
1864             //                  fResultBuffer.append("<img src=\"space/1/2004-11-21/5/");
1865             fResultBuffer.append("<img src=\"");
1866             fResultBuffer.append(FilterUtil.createServerImage(null, name.substring(6), null));
1867             fResultBuffer.append("\">");
1868           } else if (fWikiEngine != null && fWikiEngine instanceof WikiRenderEngine) {
1869             if (((WikiRenderEngine) fWikiEngine).exists(name)) {
1870               String view = name + suffix;
1871               if (-1 != pipeIndex) {
1872                 view = alias + suffix;
1873               }
1874               if (name.startsWith("Image:")) {
1875                 fResultBuffer.append("<img src=\"");
1876
1877                 fResultBuffer.append(FilterUtil.createHTMLLink(null, name, null));
1878                 fResultBuffer.append("\">");
1879               } else {
1880                 // Do not add hash if an alias was given
1881                 if (-1 != hashIndex) {
1882                   ((WikiRenderEngine) fWikiEngine).appendLink(fResultBuffer, name, view, hash);
1883                 } else {
1884                   ((WikiRenderEngine) fWikiEngine).appendLink(fResultBuffer, name, view);
1885                 }
1886               }
1887             } else if (((WikiRenderEngine) fWikiEngine).showCreate()) {
1888               ((WikiRenderEngine) fWikiEngine).appendCreateLink(fResultBuffer, name, name + suffix);
1889               // links with "create" are not cacheable because
1890               // a missing wiki could be created
1891               fContext.getRenderContext().setCacheable(false);
1892             } else {
1893               // cannot display/create wiki, so just display
1894               // the text
1895               fResultBuffer.append(name);
1896             }
1897           } else {
1898             // cannot display/create wiki, so just display the text
1899             fResultBuffer.append(name);
1900           }
1901         }
1902       }
1903     }
1904   }
1905
1906   public String createAnchor(String head) {
1907     StringBuffer result = new StringBuffer(head.length() + 1);
1908     char ch;
1909     result.append('a');
1910     // reduce Anchorstring
1911     for (int i = 0; i < head.length(); i++) {
1912       ch = head.charAt(i);
1913       if ('a' <= ch && 'z' >= ch) {
1914         result.append(ch);
1915       } else if ('A' <= ch && 'Z' >= ch) {
1916         result.append(ch);
1917       } else if ('0' <= ch && '9' >= ch) {
1918         result.append(ch);
1919       }
1920       //        switch (ch) {
1921       //          case ' ' :
1922       //            result.append('_');
1923       //            break;
1924       //          case '<' : // special html escape character
1925       //            fResult.append(Encoder.toEntity('<'));
1926       //            break;
1927       //          case '>' : // special html escape character
1928       //            fResult.append(Encoder.toEntity('>'));
1929       //            break;
1930       //          case '&' : // special html escape character
1931       //            fResult.append(Encoder.toEntity('&'));
1932       //            break;
1933       //          case '\'' : // special html escape character
1934       //            fResult.append(Encoder.toEntity('\''));
1935       //            break;
1936       //          case '\"' : // special html escape character
1937       //            fResult.append(Encoder.toEntity('\"'));
1938       //            break;
1939       //          default :
1940       //            result.append(ch);
1941       //        }
1942     }
1943     return result.toString();
1944   }
1945
1946   public static StringBuffer appendLink(StringBuffer buffer, String name, String view, String target) {
1947     return appendLinkWithRoot(buffer, null, name + "#" + target, view);
1948   }
1949
1950   /**
1951    * Create a link with a root and a special view. The name will not be url encoded!
1952    */
1953   public static StringBuffer appendLinkWithRoot(StringBuffer buffer, String root, String name, String view) {
1954     buffer.append("<a href=\"");
1955     if (root != null) {
1956       buffer.append(root);
1957       buffer.append("/");
1958     }
1959     buffer.append(name);
1960     buffer.append("\">");
1961     buffer.append(Encoder.escape(view));
1962     buffer.append("</a>");
1963     return buffer;
1964   }
1965
1966   /**
1967    * add an entry to the "table of content" TODO refactor this to a class
1968    * 
1969    * @param toc
1970    * @param head
1971    * @param anchor
1972    * @param headLevel
1973    */
1974   private void addToTableOfContent(ArrayList toc, String head, String anchor, int headLevel) {
1975     int level = 1;
1976     if (level == headLevel) {
1977       String snipName = "";
1978       //      if (fSnip != null) {
1979       //        snipName = fSnip.getName();
1980       //      }
1981
1982       StringBuffer link = new StringBuffer(snipName.length() + 40 + head.length() + anchor.length());
1983       link.append("<li>");
1984       //TODO create link for table of content
1985       appendLink(link, snipName, head, anchor);
1986       link.append("</li>");
1987       toc.add(link.toString());
1988     } else {
1989       if (toc.size() > 0) {
1990         if (toc.get(toc.size() - 1) instanceof ArrayList) {
1991           addToTableOfContent((ArrayList) toc.get(toc.size() - 1), head, anchor, --headLevel);
1992           return;
1993         }
1994       }
1995       ArrayList list = new ArrayList();
1996       toc.add(list);
1997       addToTableOfContent(list, head, anchor, --headLevel);
1998     }
1999   }
2000
2001   /**
2002    * handle head for table of content
2003    * 
2004    * @param head
2005    * @param headLevel
2006    */
2007   private void handleHead(String head, int headLevel) {
2008     if (head != null) {
2009       String anchor = createAnchor(head.trim());
2010
2011       if (fTableOfContent == null) {
2012         // create new table of content
2013         fTableOfContent = new ArrayList();
2014         // copy fResult and new initialization:
2015
2016         fResultBufferHeader = fResultBuffer;
2017         fResultBuffer = new StringBuffer(fResultBuffer.capacity());
2018       }
2019       addToTableOfContent(fTableOfContent, head, anchor, headLevel);
2020
2021       fResultBuffer.append("<h");
2022       fResultBuffer.append(headLevel);
2023       fResultBuffer.append("><a name=\"");
2024       fResultBuffer.append(anchor);
2025       fResultBuffer.append("\">");
2026       fResultBuffer.append(head);
2027       fResultBuffer.append("</a></h");
2028       fResultBuffer.append(headLevel);
2029       fResultBuffer.append(">");
2030       //      if (headLevel <= 2) {
2031       //        fResultBuffer.append("<hr/>");
2032       //      }
2033     }
2034   }
2035
2036   private boolean getList(char listChar, String openTag, String closeTag) {
2037     int currentPosition = fCurrentPosition;
2038     int level = getNumberOfChar(listChar) + 1;
2039     if (getNextChar('.') && getNextChar(' ')) {
2040       int tempPosition = checkWhitespaces(fWhiteStartPosition, fCurrentPosition - 3 - level);
2041       if (tempPosition >= 0) {
2042         copyWhite(fWhiteStart, fWhiteStartPosition, 2 + level);
2043         fWhiteStart = false;
2044         AbstractTag tok = (AbstractTag) fTokenStack.peek();
2045         if (tok instanceof ListToken) {
2046           ListToken listToken = (ListToken) tok;
2047           int topLevel = listToken.getLevel();
2048           if (listToken.getToken() == WikipediaFilter.TokenLIST_OL_START) {
2049             if (level > topLevel) {
2050               fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_OL_START, topLevel + 1));
2051               fResultBuffer.append(openTag + "<li>");
2052             } else if (level < topLevel) {
2053               fTokenStack.pop();
2054               fResultBuffer.append("</li>" + closeTag + "</li><li>");
2055             } else {
2056               fResultBuffer.append("</li><li>");
2057             }
2058           } else {
2059             fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_OL_START, level));
2060             fResultBuffer.append(openTag + "<li>");
2061           }
2062         } else {
2063           fTokenStack.push(new ListToken(WikipediaFilter.TokenLIST_OL_START, 1));
2064           fResultBuffer.append("\n" + openTag + "<li>");
2065         }
2066         return true;
2067       }
2068     }
2069     fCurrentPosition = currentPosition;
2070     return false;
2071   }
2072
2073   /**
2074    * read until the string is found
2075    * 
2076    * @param name
2077    * @return
2078    */
2079   private final boolean readUntilString(String testedString) {
2080     int temp = fCurrentPosition;
2081     int index = fStringSource.indexOf(testedString, fCurrentPosition);
2082     if (index != (-1)) {
2083       fCurrentPosition = index + testedString.length();
2084       return true;
2085     }
2086     return false;
2087   }
2088
2089   /**
2090    * read until character is found
2091    * 
2092    * @param name
2093    * @return
2094    */
2095   private final boolean readUntilChar(char testedChar) {
2096     int temp = fCurrentPosition;
2097     try {
2098       while ((fCurrentCharacter = fSource[fCurrentPosition++]) != testedChar) {
2099       }
2100       return true;
2101     } catch (IndexOutOfBoundsException e) {
2102       fCurrentPosition = temp;
2103       return false;
2104     }
2105   }
2106
2107   /**
2108    * read until character is found or end-of-line is reached
2109    * 
2110    * @param name
2111    * @return -1 - for IndexOutOfBoundsException; 0 - for LF found; 1 - for testedChar found
2112    */
2113   private final int readUntilCharOrEOL(char testedChar) {
2114     int temp = fCurrentPosition;
2115     try {
2116       while ((fCurrentCharacter = fSource[fCurrentPosition++]) != testedChar) {
2117         //                              if (fCurrentCharacter == '\n') {
2118         //                                      return 0;
2119         //                              }
2120       }
2121       return 1;
2122     } catch (IndexOutOfBoundsException e) {
2123       fCurrentPosition = temp;
2124       return -1;
2125     }
2126   }
2127
2128   /**
2129    * read until character is found or end-of-line is reached
2130    * 
2131    * @param name
2132    * @return -1 - for IndexOutOfBoundsException; 0 - for LF found; 1 - for testedChar found
2133    */
2134   private final boolean readUntilEOL() {
2135     int temp = fCurrentPosition;
2136     try {
2137       while (true) {
2138         fCurrentCharacter = fSource[fCurrentPosition++];
2139         if (fCurrentCharacter == '\n' || fCurrentCharacter == '\r') {
2140           return true;
2141         }
2142       }
2143     } catch (IndexOutOfBoundsException e) {
2144       --fCurrentPosition;
2145       return true;
2146     }
2147   }
2148
2149   /**
2150    * Returns the view of the wiki name that is shown to the fUser. Overwrite to support other views for example transform
2151    * "WikiLinking" to "Wiki Linking". Does nothing by default.
2152    * 
2153    * @return view The view of the wiki name
2154    */
2155   //  protected String getWikiView(String name) {
2156   //    return name;
2157   //  }
2158   private void handleMacro(String completeMacroSubString, String command, String unsplittedMacroParameters, String group3) {
2159     if (command != null) {
2160       // {$peng} are variables not macros.
2161       if (!command.startsWith("$")) {
2162         MacroParameter mParams = fContext.getMacroParameter();
2163
2164         if (group3 != null) {
2165           mParams.setContent(group3);
2166           mParams.setContentStart(0);
2167           mParams.setContentEnd(group3.length());
2168         }
2169         if (unsplittedMacroParameters != null && unsplittedMacroParameters.length() > 1) {
2170           //            mParams.setParams(parseParameters(unsplittedMacroParameters));
2171           mParams.setParams(unsplittedMacroParameters);
2172         }
2173         mParams.setStart(0);
2174         mParams.setEnd(completeMacroSubString.length());
2175
2176         // @DANGER: recursive calls may replace macros in included
2177         // source code
2178         try {
2179           if (fMacros.containsKey(command)) {
2180             Macro macro = (Macro) fMacros.get(command);
2181
2182             // recursively filter macros within macros
2183             if (null != mParams.getContent() && !(macro instanceof INoParserBodyFilterMacro)) {
2184               mParams.setContent(WikipediaFilter.filterParser(mParams.getContent(), fContext, fMacros, fRecursionLevel));
2185             }
2186             StringBufferWriter writer = new StringBufferWriter(new StringBuffer(256));
2187             macro.execute(writer, mParams);
2188             StringBuffer buffer = writer.getBuffer();
2189             if (macro instanceof IRenderResultMacro) {
2190               fResultBuffer.append(WikipediaFilter.filterParser(buffer.toString(), fContext, fMacros, fRecursionLevel));
2191             } else {
2192               fResultBuffer.append(buffer);
2193             }
2194
2195           } else if (command.startsWith("!")) {
2196
2197             RenderEngine engine = fContext.getRenderContext().getRenderEngine();
2198             if (engine instanceof IncludeRenderEngine) {
2199               String include = ((IncludeRenderEngine) engine).include(command.substring(1));
2200               if (null != include) {
2201                 // Filter paramFilter = new
2202                 // ParamFilter(mParams);
2203                 // included = paramFilter.filter(included,
2204                 // null);
2205                 //                  fResult.append(include);
2206
2207                 fResultBuffer.append(WikipediaFilter.filterParser(include, fContext, fMacros, fRecursionLevel));
2208
2209               } else {
2210                 fResultBuffer.append(command.substring(1) + " not found.");
2211               }
2212             }
2213
2214             return;
2215           } else {
2216             //              fResult.append(group0);
2217             copyWhite(completeMacroSubString);
2218             return;
2219           }
2220         } catch (IllegalArgumentException e) {
2221
2222           fResultBuffer.append("<div class=\"error\">" + command + ": " + e.getMessage() + "</div>");
2223
2224           e.printStackTrace();
2225
2226         } catch (Throwable e) {
2227           //          log.warn("MacroFilter: unable to format macro: " + command, e);
2228           fResultBuffer.append("<div class=\"error\">" + command + ": " + e.getMessage() + "</div>");
2229           e.printStackTrace();
2230           return;
2231         }
2232       } else {
2233         fResultBuffer.append("<");
2234         fResultBuffer.append(command.substring(1));
2235         fResultBuffer.append(">");
2236       }
2237     } else {
2238       //        fResult.append(group0);
2239       copyWhite(completeMacroSubString);
2240     }
2241   }
2242
2243   public void parse() {
2244     int token = WikipediaFilter.TokenSTART;
2245     fTokenStack.add(WikipediaFilter.START);
2246     //    fListStack.add(START);
2247     try {
2248       while ((token = getNextToken()) != WikipediaFilter.TokenEOF) {
2249         switch (token) {
2250         case WikipediaFilter.TokenBOLD:
2251           if (fTokenStack.peek() == WikipediaFilter.BOLD) {
2252             fTokenStack.pop();
2253             fResultBuffer.append("</b>");
2254           } else {
2255             fTokenStack.push(WikipediaFilter.BOLD);
2256             fResultBuffer.append("<b>");
2257           }
2258           break;
2259         case WikipediaFilter.TokenITALIC:
2260           if (fTokenStack.peek() == WikipediaFilter.ITALIC) {
2261             fTokenStack.pop();
2262             fResultBuffer.append("</i>");
2263           } else {
2264             fTokenStack.push(WikipediaFilter.ITALIC);
2265             fResultBuffer.append("<i>");
2266           }
2267           break;
2268         case WikipediaFilter.TokenSTRONG:
2269           if (fTokenStack.peek() == WikipediaFilter.STRONG) {
2270             fTokenStack.pop();
2271             fResultBuffer.append("</strong>");
2272           } else {
2273             fTokenStack.push(WikipediaFilter.STRONG);
2274             fResultBuffer.append("<strong>");
2275           }
2276           break;
2277         case WikipediaFilter.TokenEM:
2278           if (fTokenStack.peek() == WikipediaFilter.EM) {
2279             fTokenStack.pop();
2280             fResultBuffer.append("</em>");
2281           } else {
2282             fTokenStack.push(WikipediaFilter.EM);
2283             fResultBuffer.append("<em>");
2284           }
2285           break;
2286         case WikipediaFilter.TokenSTRIKETHROUGH:
2287           if (fTokenStack.peek() == WikipediaFilter.STRIKETHROUGH) {
2288             fTokenStack.pop();
2289             fResultBuffer.append("</strike>");
2290           } else {
2291             fTokenStack.push(WikipediaFilter.STRIKETHROUGH);
2292             fResultBuffer.append("<strike>");
2293           }
2294           break;
2295         //            case TokenLIST_UL_START :
2296         //              if (fTokenStack.peek().equals(LIST_UL_START)) {
2297         //                fResult.append("</li>\n<li>");
2298         //              } else {
2299         //                fTokenStack.push(LIST_UL_START);
2300         //                fResult.append("\n<ul class=\"star\">\n<li>");
2301         //              }
2302         //              break;
2303         //            case TokenLIST_UL_END :
2304         //              fTokenStack.pop();
2305         //              fResult.append("</li>\n</ul>\n");
2306         //              break;
2307         //            case TokenLIST_OL_START :
2308         //              if (fTokenStack.peek().equals(LIST_OL_START)) {
2309         //                fResult.append("</li>\n<li>");
2310         //              } else {
2311         //                fTokenStack.push(LIST_OL_START);
2312         //                fResult.append("\n<ol>\n<li>");
2313         //              }
2314         //              break;
2315         //            case TokenLIST_OL_END :
2316         //              fTokenStack.pop();
2317         //              fResult.append("</li>\n</ol>\n");
2318         //              break;
2319         }
2320       }
2321     } catch (InvalidInputException e) {
2322       //
2323     }
2324     // clear rest of stack if necessary (case of error in syntax!?)
2325     AbstractTag tok;
2326     while ((tok = (AbstractTag) fTokenStack.pop()) != WikipediaFilter.START) {
2327       if (tok instanceof OpenTagToken) {
2328
2329         CloseTagToken closeToken = (CloseTagToken) WikipediaFilter.CLOSE_TAGS.get(tok.getTagName());
2330         if (closeToken == null) {
2331           // here is something wrong ???
2332           fResultBuffer.append("</" + (tok.getTagName()) + ">");
2333         } else {
2334           fResultBuffer.append(closeToken.getCloseTag());
2335         }
2336       } else if (tok == WikipediaFilter.BOLD) {
2337         fResultBuffer.append("</b>");
2338       } else if (tok == WikipediaFilter.ITALIC) {
2339         fResultBuffer.append("</i>");
2340       } else if (tok == WikipediaFilter.STRONG) {
2341         fResultBuffer.append("</strong>");
2342       } else if (tok == WikipediaFilter.EM) {
2343         fResultBuffer.append("</em>");
2344       } else if (tok == WikipediaFilter.STRIKETHROUGH) {
2345         fResultBuffer.append("</strike>");
2346       } else if (tok.equals(WikipediaFilter.LIST_UL_START)) {
2347         fResultBuffer.append("</li>\n</ul>\n");
2348       } else if (tok.equals(WikipediaFilter.LIST_OL_START)) {
2349         fResultBuffer.append("</li>\n</ol>\n");
2350       }
2351     }
2352
2353     if (fResultBufferHeader != null) {
2354       int tocStart = fResultBufferHeader.length();
2355       if (isToC(fTableOfContent) > 3) {
2356         fResultBufferHeader.append("<table id=\"toc\" border=\"0\"><tr><th>Table of contents</th></tr><tr><td>");
2357         fResultBufferHeader.append("<ol>");
2358         createToC(fTableOfContent);
2359         fResultBufferHeader.append("</ol>");
2360         fResultBufferHeader.append("</td></tr></table><hr/>");
2361       }
2362
2363       fResultBufferHeader.append(fResultBuffer);
2364       fResultBuffer = fResultBufferHeader;
2365       fResultBufferHeader = null;
2366       fTableOfContent = null;
2367     }
2368   }
2369
2370   /**
2371    * count the number of wiki headers in this document
2372    * 
2373    * @param toc
2374    * @return
2375    */
2376   private int isToC(ArrayList toc) {
2377
2378     if (toc.size() == 1 && (toc.get(0) instanceof ArrayList)) {
2379       return isToC((ArrayList) toc.get(0));
2380     }
2381     int result = 0;
2382     for (int i = 0; i < toc.size(); i++) {
2383       if (toc.get(i) instanceof ArrayList) {
2384         result += isToC((ArrayList) toc.get(i));
2385       } else {
2386         result++;
2387       }
2388     }
2389     return result;
2390   }
2391
2392   private void createToC(ArrayList toc) {
2393     if (toc.size() == 1 && (toc.get(0) instanceof ArrayList)) {
2394       createToC((ArrayList) toc.get(0));
2395       return;
2396     }
2397     for (int i = 0; i < toc.size(); i++) {
2398       if (toc.get(i) instanceof ArrayList) {
2399         fResultBufferHeader.append("<ol>");
2400         createToC((ArrayList) toc.get(i));
2401         fResultBufferHeader.append("</ol>");
2402       } else {
2403         fResultBufferHeader.append(toc.get(i));
2404       }
2405     }
2406   }
2407
2408   //  public int readUntil(String testString) throws InvalidInputException {
2409   //    startPosition = currentPosition;
2410   //    int tempPosition;
2411   //    boolean flag;
2412   //    try {
2413   //      while (true) {
2414   //        currentCharacter = source[currentPosition++];
2415   //        if (currentCharacter == testString.charAt(0)) {
2416   //          tempPosition = currentPosition;
2417   //          flag = true;
2418   //          for (int i = 1; i < testString.length(); i++) {
2419   //            currentCharacter = source[currentPosition++];
2420   //            if (currentCharacter != testString.charAt(i)) {
2421   //              flag = false;
2422   //              currentPosition = tempPosition;
2423   //              break;
2424   //            }
2425   //          }
2426   //          if (flag) {
2427   //            return TokenBODY;
2428   //          }
2429   //        }
2430   //      }
2431   //    } catch (IndexOutOfBoundsException e) {
2432   //      // end of scanner text
2433   //    }
2434   //    return TokenEOF;
2435   //  }
2436
2437   public int scanIdentifierOrKeyword(boolean isVariable) throws InvalidInputException {
2438     while (getNextCharAsWikiPluginIdentifierPart()) {
2439     }
2440     ;
2441     return WikipediaFilter.TokenIdentifier;
2442   }
2443
2444   private final void setSource(char[] source) {
2445     //the source-buffer is set to sourceString
2446     if (source == null) {
2447       this.fSource = new char[0];
2448     } else {
2449       this.fSource = source;
2450     }
2451     //      this.fEOFPosition = this.fSource.length;
2452     //      fStartPosition = -1;
2453   }
2454
2455   private void unexpectedTag(String tag) {
2456     fResultBuffer.append("<div class=\"error\">Unexpected end for tag: &lt;" + tag + "&gt;</div>");
2457   }
2458
2459   /**
2460    * @return Returns the context.
2461    */
2462   public FilterContext getContext() {
2463     return fContext;
2464   }
2465
2466   /**
2467    * @param context
2468    *          The context to set.
2469    */
2470   public void setContext(FilterContext context) {
2471     fContext = context;
2472   }
2473
2474   /**
2475    * @return Returns the wikiEngine.
2476    */
2477   public RenderEngine getWikiEngine() {
2478     return fWikiEngine;
2479   }
2480
2481   /**
2482    * @param wikiEngine
2483    *          The wikiEngine to set.
2484    */
2485   public void setWikiEngine(RenderEngine wikiEngine) {
2486     fWikiEngine = wikiEngine;
2487   }
2488 }