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