dc7f3ef025324e4797d4147c1f6b6d806295f195
[phpeclipse.git] / archive / org.plog4u.wiki / src / org / plog4u / wiki / filter / WikipediaFilter.java
1 package org.plog4u.wiki.filter;
2
3 import java.io.IOException;
4 import java.io.Writer;
5 import java.text.MessageFormat;
6 import java.util.HashMap;
7 import java.util.HashSet;
8
9
10 import org.plog4u.wiki.filter.tags.AbstractTag;
11 import org.plog4u.wiki.filter.tags.CloseTagToken;
12 import org.plog4u.wiki.filter.tags.DivTag;
13 import org.plog4u.wiki.filter.tags.FontTag;
14 import org.plog4u.wiki.filter.tags.OpenTagToken;
15 import org.plog4u.wiki.filter.tags.SpecialTagToken;
16 import org.plog4u.wiki.filter.tags.TableTag;
17 import org.radeox.api.engine.ImageRenderEngine;
18 import org.radeox.api.engine.IncludeRenderEngine;
19 import org.radeox.api.engine.RenderEngine;
20 import org.radeox.api.engine.WikiRenderEngine;
21 import org.radeox.filter.CacheFilter;
22 import org.radeox.filter.FilterSupport;
23 import org.radeox.filter.context.FilterContext;
24 import org.radeox.filter.interwiki.InterWiki;
25 import org.radeox.macro.Macro;
26 import org.radeox.macro.MacroRepository;
27 import org.radeox.macro.parameter.MacroParameter;
28 import org.radeox.util.Encoder;
29 import org.radeox.util.StringBufferWriter;
30
31 //import org.snipsnap.app.Application;
32 //import org.snipsnap.fUser.Configuration;
33 //import org.snipsnap.app.Application;
34
35 /**
36  * Parse the input and transform it for the most used wiki patterns
37  * 
38  * Parts of the Wiki Syntax are borrowed from Wikipedia.org and SnipSnap.org
39  * 
40  * @see org.plog4u.wiki.filter.WikipediaParser
41  */
42 public class WikipediaFilter extends FilterSupport implements CacheFilter, IWikipediaFilterConstants {
43   
44   public class InvalidInputException extends Exception {  
45
46     public InvalidInputException() {
47       super();
48     }
49
50     public InvalidInputException(String s) {
51       super(s);
52     }
53   }
54
55   
56   
57 //  public static class ListToken extends Token {
58 //    int fLevel;
59 //
60 //    public ListToken(int token, int level) {
61 //      super(token);
62 //      fLevel = level;
63 //    }
64 //
65 //    public int getLevel() {
66 //      return fLevel;
67 //    }
68 //  }
69 //
70 //  public static class OpenTagToken extends Token {
71 //    private String fTagName;
72 //    private String fOpenTag;
73 //
74 //    public OpenTagToken(int token, String name, String openTag) {
75 //      super(token);
76 //      fTagName = name;
77 //      fOpenTag = openTag;
78 //
79 //    }
80 //
81 //    public String getTagName() {
82 //      return fTagName;
83 //    }
84 //
85 //    public String getOpenTag() {
86 //      return fOpenTag;
87 //    }
88 //
89 //    public void setTagName(String name) {
90 //      fTagName = name;
91 //    }
92 //
93 //    public void setOpenTag(String openTag) {
94 //      fOpenTag = openTag;
95 //    }
96 //  }
97 //
98 //  public static class SpecialTagToken extends OpenTagToken {
99 //    public SpecialTagToken(int token, String name, String openTag) {
100 //      super(token, name, openTag);
101 //    }
102 //  }
103 //
104 //  public static class CloseTagToken extends Token {
105 //    private String fTagName;
106 //    private String fCloseTag;
107 //
108 //    public CloseTagToken(int token, String name, String closeTag) {
109 //      super(token);
110 //      fTagName = name;
111 //      fCloseTag = closeTag;
112 //    }
113 //
114 //    public String getTagName() {
115 //      return fTagName;
116 //    }
117 //
118 //    public String getCloseTag() {
119 //      return fCloseTag;
120 //    }
121 //
122 //    public void setTagName(String name) {
123 //      fTagName = name;
124 //    }
125 //
126 //    public void setCloseTag(String closeTag) {
127 //      fCloseTag = closeTag;
128 //    }
129 //
130 //  }
131   final static String HEADER_STRINGS[] = { "=", "==", "===", "====", "=====", "======" };
132
133   final static int TokenNotFound = -2;
134   final static int TokenIgnore = -1;
135   final static int TokenSTART = 0;
136   final static int TokenEOF = 1;
137   final static int TokenERROR = 2;
138   final static int TokenBOLD = 3;
139   final static int TokenITALIC = 4;
140   final static int TokenSTRIKETHROUGH = 5;
141   final static int TokenSTRONG = 8;
142   final static int TokenEM = 9;
143
144   final static int TokenCOMMA = 134;
145
146   final static int TokenDOLLAR_LBRACE = 127;
147
148   final static int TokenLIST_UL_START = 50;
149   final static int TokenLIST_OL_START = 51;
150
151   final static int TokenLIST_UL_END = 75;
152   final static int TokenLIST_OL_END = 76;
153
154   final static int TokenHTML_BR_OPEN = 190;
155   final static int TokenHTML_HR_OPEN = 191;
156
157   final static int TokenHTML_BOLD_OPEN = 200;
158   final static int TokenHTML_BOLD_CLOSE = 201;
159   final static int TokenHTML_ITALIC_OPEN = 202;
160   final static int TokenHTML_ITALIC_CLOSE = 203;
161   final static int TokenHTML_UNDERLINE_OPEN = 204;
162   final static int TokenHTML_UNDERLINE_CLOSE = 205;
163   final static int TokenHTML_STRIKE_OPEN = 206;
164   final static int TokenHTML_STRIKE_CLOSE = 207;
165   final static int TokenHTML_PARAGRAPH_OPEN = 208;
166   final static int TokenHTML_PARAGRAPH_CLOSE = 209;
167   final static int TokenHTML_PRE_OPEN = 210;
168   final static int TokenHTML_PRE_CLOSE = 211;
169   final static int TokenHTML_BLOCKQUOTE_OPEN = 212;
170   final static int TokenHTML_BLOCKQUOTE_CLOSE = 213;
171   final static int TokenHTML_SUB_OPEN = 216;
172   final static int TokenHTML_SUB_CLOSE = 217;
173   final static int TokenHTML_SUP_OPEN = 218;
174   final static int TokenHTML_SUP_CLOSE = 219;
175
176   final static int TokenHTML_H1_CLOSE = 221;
177
178   final static int TokenHTML_H1_OPEN = 220;
179   final static int TokenHTML_H2_CLOSE = 223;
180   final static int TokenHTML_H2_OPEN = 222;
181   final static int TokenHTML_H3_CLOSE = 225;
182   final static int TokenHTML_H3_OPEN = 224;
183   final static int TokenHTML_H4_CLOSE = 227;
184   final static int TokenHTML_H4_OPEN = 226;
185   final static int TokenHTML_H5_CLOSE = 229;
186   final static int TokenHTML_H5_OPEN = 228;
187   final static int TokenHTML_H6_CLOSE = 231;
188   final static int TokenHTML_H6_OPEN = 230;
189   final static int TokenHTML_EM_OPEN = 240;
190   final static int TokenHTML_EM_CLOSE = 241;
191
192   
193   final static int TokenHTML_STRONG_OPEN = 242;
194   final static int TokenHTML_STRONG_CLOSE = 243;
195   
196   final static int TokenHTML_VAR_OPEN = 245;
197   final static int TokenHTML_VAR_CLOSE = 246;
198   final static int TokenHTML_CODE_OPEN = 247;
199   final static int TokenHTML_CODE_CLOSE = 248;
200
201   final static int TokenHTML_S_OPEN = 249;
202   final static int TokenHTML_S_CLOSE = 250;
203   final static int TokenHTML_SMALL_OPEN = 251;
204   final static int TokenHTML_SMALL_CLOSE = 252;
205   
206   final static int TokenHTML_MATH_OPEN = 400;
207   final static int TokenHTML_MATH_CLOSE = 401;
208   
209   final static int TokenHTML_TABLE_OPEN = 500;
210   final static int TokenHTML_TABLE_CLOSE = 501;
211   final static int TokenHTML_CAPTION_OPEN = 502;
212   final static int TokenHTML_CAPTION_CLOSE = 503;
213   final static int TokenHTML_TH_OPEN = 504;
214   final static int TokenHTML_TH_CLOSE = 505;
215   final static int TokenHTML_TR_OPEN = 506;
216   final static int TokenHTML_TR_CLOSE = 507;
217   final static int TokenHTML_TD_OPEN = 508;
218   final static int TokenHTML_TD_CLOSE = 509;
219   
220   final static int TokenHTML_FONT_OPEN = 520;
221   final static int TokenHTML_FONT_CLOSE = 521;
222   final static int TokenHTML_CENTER_OPEN = 522;
223   final static int TokenHTML_CENTER_CLOSE = 523;
224   final static int TokenHTML_TT_OPEN = 524;
225   final static int TokenHTML_TT_CLOSE = 525;
226   final static int TokenHTML_DIV_OPEN = 526;
227   final static int TokenHTML_DIV_CLOSE = 527;
228   
229   final static int TokenIdentifier = 138;
230   final static int TokenLBRACKET = 132;
231
232   final static int TokenLPAREN = 128;
233
234   final static int TokenPLUGIN_IDENTIFIER = 130;
235   final static int TokenRBRACE = 131;
236   final static int TokenRBRACKET = 133;
237   final static int TokenRPAREN = 129;
238
239   final static AbstractTag BOLD = new AbstractTag(TokenBOLD);
240   final static AbstractTag ITALIC = new AbstractTag(TokenITALIC);
241   final static AbstractTag STRONG = new AbstractTag(TokenSTRONG);
242   final static AbstractTag EM = new AbstractTag(TokenEM);
243
244   final static AbstractTag HTML_BR_OPEN = new SpecialTagToken(TokenHTML_BR_OPEN, "br", "<br/>");
245   final static AbstractTag HTML_HR_OPEN = new SpecialTagToken(TokenHTML_HR_OPEN, "hr", "<hr/>");
246
247   final static AbstractTag HTML_EM_CLOSE = new CloseTagToken(TokenHTML_EM_CLOSE, "em", "</i>");
248   final static AbstractTag HTML_EM_OPEN = new OpenTagToken(TokenHTML_EM_OPEN, "em", "<i>");
249   final static AbstractTag HTML_H1_CLOSE = new CloseTagToken(TokenHTML_H1_CLOSE, "h1", "</h1>");
250   final static AbstractTag HTML_H1_OPEN = new OpenTagToken(TokenHTML_H1_OPEN, "h1", "<h1>");
251   final static AbstractTag HTML_H2_CLOSE = new CloseTagToken(TokenHTML_H2_CLOSE, "h2", "</h2>");
252   final static AbstractTag HTML_H2_OPEN = new OpenTagToken(TokenHTML_H2_OPEN, "h2", "<h2>");
253   final static AbstractTag HTML_H3_CLOSE = new CloseTagToken(TokenHTML_H3_CLOSE, "h3", "</h3>");
254   final static AbstractTag HTML_H3_OPEN = new OpenTagToken(TokenHTML_H3_OPEN, "h3", "<h3>");
255   final static AbstractTag HTML_H4_CLOSE = new CloseTagToken(TokenHTML_H4_CLOSE, "h4", "</h4>");
256   final static AbstractTag HTML_H4_OPEN = new OpenTagToken(TokenHTML_H4_OPEN, "h4", "<h4>");
257   final static AbstractTag HTML_H5_CLOSE = new CloseTagToken(TokenHTML_H5_CLOSE, "h5", "</h5>");
258   final static AbstractTag HTML_H5_OPEN = new OpenTagToken(TokenHTML_H5_OPEN, "h5", "<h5>");
259   final static AbstractTag HTML_H6_CLOSE = new CloseTagToken(TokenHTML_H6_CLOSE, "h6", "</h6>");
260   final static AbstractTag HTML_H6_OPEN = new OpenTagToken(TokenHTML_H6_OPEN, "h6", "<h6>");
261   final static AbstractTag HTML_ITALIC_CLOSE = new CloseTagToken(TokenHTML_ITALIC_CLOSE, "i", "</i>");
262   final static AbstractTag HTML_ITALIC_OPEN = new OpenTagToken(TokenHTML_ITALIC_OPEN, "i", "<i>");
263   final static AbstractTag HTML_BOLD_CLOSE = new CloseTagToken(TokenHTML_BOLD_CLOSE, "b", "</b>");
264   final static AbstractTag HTML_BOLD_OPEN = new OpenTagToken(TokenHTML_BOLD_OPEN, "b", "<b>");
265
266   //
267   final static AbstractTag HTML_PARAGRAPH_CLOSE = new CloseTagToken(TokenHTML_PARAGRAPH_CLOSE, "p", "</p>");
268   final static AbstractTag HTML_PARAGRAPH_OPEN = new OpenTagToken(TokenHTML_PARAGRAPH_OPEN, "p", "<p>");
269   final static AbstractTag HTML_PRE_CLOSE = new CloseTagToken(TokenHTML_PRE_CLOSE, "pre", "</pre>");
270   final static AbstractTag HTML_PRE_OPEN = new OpenTagToken(TokenHTML_PRE_OPEN, "pre", "<pre>");
271   final static AbstractTag HTML_BLOCKQUOTE_CLOSE = new CloseTagToken(TokenHTML_BLOCKQUOTE_CLOSE, "blockquote", "</blockquote>");
272   final static AbstractTag HTML_BLOCKQUOTE_OPEN = new OpenTagToken(TokenHTML_BLOCKQUOTE_OPEN, "blockquote", "<blockquote>");
273   final static AbstractTag HTML_STRIKE_CLOSE = new CloseTagToken(TokenHTML_STRIKE_CLOSE, "strike", "</strike>");
274   final static AbstractTag HTML_STRIKE_OPEN = new OpenTagToken(TokenHTML_STRIKE_OPEN, "strike", "<strike>");
275   final static AbstractTag HTML_STRONG_CLOSE = new CloseTagToken(TokenHTML_STRONG_CLOSE, "strong", "</b>");
276   final static AbstractTag HTML_STRONG_OPEN = new OpenTagToken(TokenHTML_STRONG_OPEN, "strong", "<b>");
277   final static AbstractTag HTML_UNDERLINE_CLOSE = new CloseTagToken(TokenHTML_UNDERLINE_CLOSE, "u", "</u>");
278   final static AbstractTag HTML_UNDERLINE_OPEN = new OpenTagToken(TokenHTML_UNDERLINE_OPEN, "u", "<u>");
279   final static AbstractTag HTML_SUB_CLOSE = new CloseTagToken(TokenHTML_SUB_CLOSE, "sub", "</sub>");
280   final static AbstractTag HTML_SUB_OPEN = new OpenTagToken(TokenHTML_SUB_OPEN, "sub", "<sub>");
281   final static AbstractTag HTML_SUP_CLOSE = new CloseTagToken(TokenHTML_SUP_CLOSE, "sup", "</sup>");
282   final static AbstractTag HTML_SUP_OPEN = new OpenTagToken(TokenHTML_SUP_OPEN, "sup", "<sup>");
283   
284   final static AbstractTag HTML_CENTER_OPEN = new OpenTagToken(TokenHTML_CENTER_OPEN, "center", "<center>");
285   final static AbstractTag HTML_CENTER_CLOSE = new CloseTagToken(TokenHTML_CENTER_CLOSE, "center", "</center>");
286   final static AbstractTag HTML_TT_OPEN = new OpenTagToken(TokenHTML_TT_OPEN, "tt", "<tt>");
287   final static AbstractTag HTML_TT_CLOSE = new CloseTagToken(TokenHTML_TT_CLOSE, "tt", "</tt>");
288   
289   final static AbstractTag HTML_MATH_OPEN = new OpenTagToken(TokenHTML_MATH_OPEN, "math", "<math>");
290   final static AbstractTag HTML_MATH_CLOSE = new CloseTagToken(TokenHTML_MATH_CLOSE, "math", "</math>");
291
292   final static AbstractTag HTML_TABLE_OPEN = new TableTag(TokenHTML_TABLE_OPEN, "table", "<table>");
293   final static AbstractTag HTML_TABLE_CLOSE = new CloseTagToken(TokenHTML_TABLE_CLOSE, "table", "</table>");
294   final static AbstractTag HTML_CAPTION_OPEN = new OpenTagToken(TokenHTML_CAPTION_OPEN, "caption", "<caption>");
295   final static AbstractTag HTML_CAPTION_CLOSE = new CloseTagToken(TokenHTML_CAPTION_CLOSE, "caption", "</caption>");
296   final static AbstractTag HTML_TH_OPEN = new OpenTagToken(TokenHTML_TH_OPEN, "th", "<th>");
297   final static AbstractTag HTML_TH_CLOSE = new CloseTagToken(TokenHTML_TH_CLOSE, "th", "</th>");
298   final static AbstractTag HTML_TR_OPEN = new OpenTagToken(TokenHTML_TR_OPEN, "tr", "<tr>");
299   final static AbstractTag HTML_TR_CLOSE = new CloseTagToken(TokenHTML_TR_CLOSE, "tr", "</tr>");
300   final static AbstractTag HTML_TD_OPEN = new OpenTagToken(TokenHTML_TD_OPEN, "td", "<td>");
301   final static AbstractTag HTML_TD_CLOSE = new CloseTagToken(TokenHTML_TD_CLOSE, "td", "</td>");
302   final static AbstractTag HTML_FONT_OPEN = new FontTag(TokenHTML_FONT_OPEN, "font", "<font>");
303   final static AbstractTag HTML_FONT_CLOSE = new CloseTagToken(TokenHTML_FONT_CLOSE, "font", "</font>");
304   final static AbstractTag HTML_DIV_OPEN = new DivTag(TokenHTML_DIV_OPEN, "div", "<div>");
305   final static AbstractTag HTML_DIV_CLOSE = new CloseTagToken(TokenHTML_DIV_CLOSE, "div", "</div>");
306   
307   final static AbstractTag LIST_OL_START = new AbstractTag(TokenLIST_OL_START);
308   final static AbstractTag LIST_UL_START = new AbstractTag(TokenLIST_UL_START);
309
310   final static AbstractTag HTML_VAR_OPEN = new OpenTagToken(TokenHTML_VAR_OPEN, "var", "<var>");
311   final static AbstractTag HTML_VAR_CLOSE = new CloseTagToken(TokenHTML_VAR_CLOSE, "var", "</var>");
312   final static AbstractTag HTML_CODE_OPEN = new OpenTagToken(TokenHTML_CODE_OPEN, "code", "<code>");
313   final static AbstractTag HTML_CODE_CLOSE = new CloseTagToken(TokenHTML_CODE_CLOSE, "code", "</code>");
314   
315   // strikethrough
316   final static AbstractTag HTML_S_OPEN = new OpenTagToken(TokenHTML_S_OPEN, "s", "<s>");
317   final static AbstractTag HTML_S_CLOSE = new CloseTagToken(TokenHTML_S_CLOSE, "s", "</s>");
318   // small
319   final static AbstractTag HTML_SMALL_OPEN = new OpenTagToken(TokenHTML_SMALL_OPEN, "small", "<small>");
320   final static AbstractTag HTML_SMALL_CLOSE = new CloseTagToken(TokenHTML_SMALL_CLOSE, "small", "</small>");
321
322   final static HashMap OPEN_TAGS = new HashMap();
323   final static HashMap CLOSE_TAGS = new HashMap();
324
325   final static HashSet ENTITY_SET = new HashSet();
326   final static HashMap IMAGE_MAP = new HashMap();
327
328 //  private static Log log = LogFactory.getLog(WikipediaFilter.class);
329
330   {
331     for (int i = 0; i < ENTITY_STRINGS.length; i++) {
332       ENTITY_SET.add(ENTITY_STRINGS[i]);
333     }
334     for (int i = 0; i < IMAGE_STRINGS.length; i += 2) {
335       IMAGE_MAP.put(IMAGE_STRINGS[i], IMAGE_STRINGS[i + 1]);
336     }
337     OPEN_TAGS.put("br", HTML_BR_OPEN);
338     OPEN_TAGS.put("hr", HTML_HR_OPEN);
339
340     OPEN_TAGS.put("h1", HTML_H1_OPEN);
341     OPEN_TAGS.put("h2", HTML_H2_OPEN);
342     OPEN_TAGS.put("h3", HTML_H3_OPEN);
343     OPEN_TAGS.put("h4", HTML_H4_OPEN);
344     OPEN_TAGS.put("h5", HTML_H5_OPEN);
345     OPEN_TAGS.put("h6", HTML_H6_OPEN);
346
347     CLOSE_TAGS.put("h1", HTML_H1_CLOSE);
348     CLOSE_TAGS.put("h2", HTML_H2_CLOSE);
349     CLOSE_TAGS.put("h3", HTML_H1_CLOSE);
350     CLOSE_TAGS.put("h4", HTML_H2_CLOSE);
351     CLOSE_TAGS.put("h5", HTML_H1_CLOSE);
352     CLOSE_TAGS.put("h6", HTML_H2_CLOSE);
353
354     OPEN_TAGS.put("em", HTML_EM_OPEN);
355     CLOSE_TAGS.put("em", HTML_EM_CLOSE);
356     OPEN_TAGS.put("i", HTML_ITALIC_OPEN);
357     CLOSE_TAGS.put("i", HTML_ITALIC_CLOSE);
358     OPEN_TAGS.put("b", HTML_BOLD_OPEN);
359     CLOSE_TAGS.put("b", HTML_BOLD_CLOSE);
360     OPEN_TAGS.put("strong", HTML_STRONG_OPEN);
361     CLOSE_TAGS.put("strong", HTML_STRONG_CLOSE);
362     OPEN_TAGS.put("u", HTML_UNDERLINE_OPEN);
363     CLOSE_TAGS.put("u", HTML_UNDERLINE_CLOSE);
364     OPEN_TAGS.put("p", HTML_PARAGRAPH_OPEN);
365     CLOSE_TAGS.put("p", HTML_PARAGRAPH_CLOSE);
366
367     OPEN_TAGS.put("pre", HTML_PRE_OPEN);
368     CLOSE_TAGS.put("pre", HTML_PRE_CLOSE);
369     OPEN_TAGS.put("blockquote", HTML_BLOCKQUOTE_OPEN);
370     CLOSE_TAGS.put("blockquote", HTML_BLOCKQUOTE_CLOSE);
371
372     OPEN_TAGS.put("var", HTML_VAR_OPEN);
373     CLOSE_TAGS.put("var", HTML_VAR_CLOSE);
374     OPEN_TAGS.put("code", HTML_CODE_OPEN);
375     CLOSE_TAGS.put("code", HTML_CODE_CLOSE);
376     OPEN_TAGS.put("s", HTML_S_OPEN);
377     CLOSE_TAGS.put("s", HTML_S_CLOSE);
378     OPEN_TAGS.put("small", HTML_SMALL_OPEN);
379     CLOSE_TAGS.put("small", HTML_SMALL_CLOSE);
380     OPEN_TAGS.put("sub", HTML_SUB_OPEN);
381     CLOSE_TAGS.put("sub", HTML_SUB_CLOSE);
382     OPEN_TAGS.put("sup", HTML_SUP_OPEN);
383     CLOSE_TAGS.put("sup", HTML_SUP_CLOSE);
384     OPEN_TAGS.put("strike", HTML_STRIKE_OPEN);
385     CLOSE_TAGS.put("strike", HTML_STRIKE_CLOSE);
386
387     OPEN_TAGS.put("math", HTML_MATH_OPEN);
388     CLOSE_TAGS.put("math", HTML_MATH_CLOSE);
389     
390     OPEN_TAGS.put("table", HTML_TABLE_OPEN);
391     CLOSE_TAGS.put("table", HTML_TABLE_CLOSE);
392     OPEN_TAGS.put("th", HTML_TH_OPEN);
393     CLOSE_TAGS.put("th", HTML_TH_CLOSE);
394     OPEN_TAGS.put("tr", HTML_TR_OPEN);
395     CLOSE_TAGS.put("tr", HTML_TR_CLOSE);
396     OPEN_TAGS.put("td", HTML_TD_OPEN);
397     CLOSE_TAGS.put("td", HTML_TD_CLOSE);
398     OPEN_TAGS.put("caption", HTML_CAPTION_OPEN);
399     CLOSE_TAGS.put("caption", HTML_CAPTION_CLOSE);
400     
401     OPEN_TAGS.put("font", HTML_FONT_OPEN);
402     CLOSE_TAGS.put("font", HTML_FONT_CLOSE);
403     OPEN_TAGS.put("center", HTML_CENTER_OPEN);
404     CLOSE_TAGS.put("center", HTML_CENTER_CLOSE);
405     OPEN_TAGS.put("tt", HTML_TT_OPEN);
406     CLOSE_TAGS.put("tt", HTML_TT_CLOSE);
407     OPEN_TAGS.put("div", HTML_DIV_OPEN);
408     CLOSE_TAGS.put("div", HTML_DIV_CLOSE);
409   }
410
411   /**
412    * Limits the recursive call of this filter to a depth of RECURSION_LIMIT
413    */
414   final static int RECURSION_LIMIT = 5;
415
416   final static AbstractTag START = new AbstractTag(TokenSTART);
417
418   final static AbstractTag STRIKETHROUGH = new AbstractTag(TokenSTRIKETHROUGH);
419
420   /**
421    * Determines if the specified character may be part of a url
422    */
423   public final static boolean isUrlIdentifierPart(char ch) {
424     if (Character.isLetterOrDigit(ch)) {
425       return true;
426     }
427     final String test = "-_.!~*';/?:@#&=+$,";
428     return test.indexOf(ch) != (-1);
429   }
430
431   /**
432    * Determines if the specified character may be part of a wiki plugin identifier as other than the first character
433    */
434   public final static boolean isWikiPluginIdentifierPart(char ch) {
435     return Character.isLetterOrDigit(ch) || (ch == '_');
436   }
437
438   /**
439    * Determines if the specified character may be part the first character of a wiki plugin identifier
440    */
441   public final static boolean isWikiPluginIdentifierStart(char ch) {
442     return Character.isLetter(ch);
443   }
444
445   public static boolean createStaticImage(String imageTag, StringBuffer buff) {
446     String value = (String) IMAGE_MAP.get(imageTag);
447     if (value != null) {
448       buff.append(MessageFormat.format(value, ARGUMENTS));
449       return true;
450     }
451     return false;
452   }
453
454   //  private MacroRepository macros;
455   
456   public WikipediaFilter() {
457     super();
458   }
459
460   public String filter(String input, FilterContext context) {
461     return filter(input, context, MacroRepository.getInstance(), 0);
462   }
463
464   public String filter(
465     String input,
466     FilterContext context,
467     MacroRepository macros,
468     int recursionLevel) {
469     try {
470       if (++recursionLevel > RECURSION_LIMIT) {
471         String error = "<span class=\"error\">Error - recursion limit exceeded.</span>";
472         return error;
473       }
474
475       StringBuffer result = new StringBuffer(input.length() + input.length() / 10);
476       if (input.startsWith("#html")) {
477         // ignore first line
478         int index = input.indexOf('\n');
479         if (index != (-1)) {
480          
481 //          Configuration fUser = Application.get().getUser();
482 //          if (fUser.isAdmin()) {
483             // admins are allowed to insert full html directly 
484             String subst = input.substring(index + 1);
485             // for page preview:
486             return subst;
487 //          }
488         }
489       }
490       // instantiate inner Parser class
491       WikipediaParser parser = new WikipediaParser(macros, input, result, context, recursionLevel);
492       parser.parse();
493       return result.toString();
494       // Util.substitute(matcher, p, new Perl5Substitution(s, interps), result, limit);
495     } catch (Exception e) {
496       //log.warn("<span class=\"error\">Exception</span>: " + this + ": " + e);
497 //      log.warn("Exception for: " + " " + e);
498       e.printStackTrace();
499     } catch (Error err) {
500       //log.warn("<span class=\"error\">Error</span>: " + this + ": " + err);
501 //      log.warn("Error for: ");
502       err.printStackTrace();
503     }
504     String error = "<span class=\"error\">Error in Parser.</span>";
505     return error;
506   }
507
508   public static String filterParser(
509     String input,
510     FilterContext context,
511     MacroRepository macros,
512     int recursionLevel) {
513     try {
514       if (++recursionLevel > RECURSION_LIMIT) {
515         return "<span class=\"error\">Error - recursion limit exceeded.</span>";
516       }
517
518       StringBuffer result = new StringBuffer(input.length() + input.length() / 10);
519       // instantiate inner Parser class
520       WikipediaParser parser = new WikipediaParser(macros, input, result, context, recursionLevel);
521       parser.parse();
522       return result.toString();
523       // Util.substitute(matcher, p, new Perl5Substitution(s, interps), result, limit);
524     } catch (Exception e) {
525       //log.warn("<span class=\"error\">Exception</span>: " + this + ": " + e);
526 //      log.warn("Exception for: " + " " + e);
527       e.printStackTrace();
528     } catch (Error err) {
529       //log.warn("<span class=\"error\">Error</span>: " + this + ": " + err);
530 //      log.warn("Error for: ");
531       err.printStackTrace();
532     }
533
534     return "<span class=\"error\">Error in Parser.</span>";
535   }
536
537   //  private void filterParams(StringBuffer buffer, String name) {
538   //    Map param = fContext.getRenderContext().getParameters();
539   //    if (param != null) {
540   //      if (param.containsKey(name)) {
541   //        Object value = param.get(name);
542   //        if (value instanceof String[]) {
543   //          buffer.append(((String[]) value)[0]);
544   //        } else {
545   //          buffer.append(value);
546   //        }
547   //      } else {
548   //        buffer.append("<");
549   //        buffer.append(name);
550   //        buffer.append(">");
551   //      }
552   //    } else {
553   //      buffer.append("<");
554   //      buffer.append(name);
555   //      buffer.append(">");
556   //    }
557   //  }
558
559   //  public String parseParameters(String unsplittedMacroParameters) {
560   //    int currPos = 0;
561   //    int currEndPos = 0;
562   //    int len = unsplittedMacroParameters.length();
563   //    StringBuffer buffer = new StringBuffer(len);
564   //    char ch;
565   //    while (currPos < len) {
566   //      ch = unsplittedMacroParameters.charAt(currPos++);
567   //      if (ch != '$') {
568   //        buffer.append(ch);
569   //      } else {
570   //        if ((currEndPos = unsplittedMacroParameters.indexOf('|', currPos)) >= 1) {
571   //          filterParams(buffer, unsplittedMacroParameters.substring(currPos, currEndPos));
572   //          currPos = currEndPos + 1;
573   //          buffer.append('|');
574   //        } else {
575   //          if (currPos < len) {
576   //            filterParams(buffer, unsplittedMacroParameters.substring(currPos, len));
577   //          }
578   //        }
579   //      }
580   //    }
581   //    return buffer.toString();
582   //  }
583
584   //  protected Repository getMacroRepository() {
585   //    return macros;
586   //  }
587
588   //  public void setInitialContext(InitialRenderContext context) {
589   //    macros = MacroRepository.getInstance();
590   //    macros.setInitialContext(context);
591   //  }
592
593   public static void copyWhite(StringBuffer result, String text) {
594     final int len = text.length();
595     int currentIndex = 0;
596     int lastIndex = currentIndex;
597     while (currentIndex < len) {
598       switch (text.charAt(currentIndex++)) {
599         case '<' : // special html escape character
600           if (lastIndex < (currentIndex - 1)) {
601             result.append(text.substring(lastIndex, currentIndex - 1));
602             lastIndex = currentIndex;
603           } else {
604             lastIndex++;
605           }
606           result.append("&#60;");
607           break;
608         case '>' : // special html escape character
609           if (lastIndex < (currentIndex - 1)) {
610             result.append(text.substring(lastIndex, currentIndex - 1));
611             lastIndex = currentIndex;
612           } else {
613             lastIndex++;
614           }
615           result.append("&#62;");
616           break;
617         case '&' : // special html escape character
618           if (lastIndex < (currentIndex - 1)) {
619             result.append(text.substring(lastIndex, currentIndex - 1));
620             lastIndex = currentIndex;
621           } else {
622             lastIndex++;
623           }
624           result.append("&#38;");
625           break;
626         case '\'' : // special html escape character
627           if (lastIndex < (currentIndex - 1)) {
628             result.append(text.substring(lastIndex, currentIndex - 1));
629             lastIndex = currentIndex;
630           } else {
631             lastIndex++;
632           }
633           result.append("&#39;");
634           break;
635         case '\"' : // special html escape character
636           if (lastIndex < (currentIndex - 1)) {
637             result.append(text.substring(lastIndex, currentIndex - 1));
638             lastIndex = currentIndex;
639           } else {
640             lastIndex++;
641           }
642           result.append("&#34;");
643           break;
644       }
645     }
646     if (lastIndex < (currentIndex)) {
647       result.append(text.substring(lastIndex, currentIndex));
648     }
649   }
650
651   public static void createMacro(StringBuffer result, String macroCommand, FilterContext context, MacroRepository macros) {
652     String command = "";
653     String endTag;
654
655     String parameterString = null;
656     String macroBodyString = "";
657     int index0 = 0;
658     int index1 = macroCommand.indexOf('}');
659     if ((index0 = macroCommand.indexOf(':')) >= 0 && (index0 < index1)) {
660       command = macroCommand.substring(1, index0);
661       parameterString = macroCommand.substring(index0 + 1, index1);
662     } else {
663       command = macroCommand.substring(1, index1);
664     }
665     Macro macro = (Macro) macros.get(command);
666
667     String completeMacroSubString;
668
669     if ((macro != null) && (macro instanceof IBodyTagSupportMacro)) {
670       endTag = '{' + command + '}';
671       index0 = macroCommand.indexOf(endTag, command.length());
672
673       if (index0 >= 0) {
674         macroBodyString = macroCommand.substring(index1 + 1, index0);
675       }
676     }
677     completeMacroSubString = macroCommand;
678
679     handleMacro(result, completeMacroSubString, command, parameterString, macroBodyString, context, macros);
680
681   }
682
683   private static void handleMacro(
684     StringBuffer result,
685     String completeMacroSubString,
686     String command,
687     String unsplittedMacroParameters,
688     String group3,
689     FilterContext context,
690     MacroRepository macros) {
691     if (command != null) {
692       // {$peng} are variables not macros.
693       if (!command.startsWith("$")) {
694         MacroParameter mParams = context.getMacroParameter();
695
696         if (group3 != null) {
697           mParams.setContent(group3);
698           mParams.setContentStart(0);
699           mParams.setContentEnd(group3.length());
700         }
701         if (unsplittedMacroParameters != null && unsplittedMacroParameters.length() > 1) {
702           //            mParams.setParams(parseParameters(unsplittedMacroParameters));
703           mParams.setParams(unsplittedMacroParameters);
704         }
705         mParams.setStart(0);
706         mParams.setEnd(completeMacroSubString.length());
707
708         // @DANGER: recursive calls may replace macros in included source code
709         try {
710           if (macros.containsKey(command)) {
711
712             Macro macro = (Macro) macros.get(command);
713             // recursively filter macros within macros
714             if (null != mParams.getContent() && !(macro instanceof INoParserBodyFilterMacro)) {
715               mParams.setContent(filterParser(mParams.getContent(), context, macros, 0));
716             }
717             StringBufferWriter writer = new StringBufferWriter(new StringBuffer(256));
718             macro.execute(writer, mParams);
719             StringBuffer buffer = writer.getBuffer();
720             if (macro instanceof IRenderResultMacro) {
721               result.append(filterParser(buffer.toString(), context, macros, 0));
722             } else {
723               result.append(buffer);
724             }
725
726           } else if (command.startsWith("!")) {
727             RenderEngine engine = context.getRenderContext().getRenderEngine();
728             if (engine instanceof IncludeRenderEngine) {
729               String include = ((IncludeRenderEngine) engine).include(command.substring(1));
730               if (null != include) {
731                 // Filter paramFilter = new ParamFilter(mParams);
732                 // included = paramFilter.filter(included, null);
733                 //                  fResult.append(include);
734                 result.append(filterParser(include, context, macros, 0));
735               } else {
736                 result.append(command.substring(1) + " not found.");
737               }
738             }
739             return;
740           } else {
741             //              fResult.append(group0);
742             copyWhite(result, completeMacroSubString);
743             return;
744           }
745         } catch (IllegalArgumentException e) {
746           result.append("<div class=\"error\">" + command + ": " + e.getMessage() + "</div>");
747
748           e.printStackTrace();
749
750         } catch (Throwable e) {
751 //          log.warn("MacroFilter: unable to format macro: " + command, e);
752           result.append("<div class=\"error\">" + command + ": " + e.getMessage() + "</div>");
753           e.printStackTrace();
754           return;
755         }
756       } else {
757         result.append("<");
758         result.append(command.substring(1));
759         result.append(">");
760       }
761     } else {
762       //        fResult.append(group0);
763       copyWhite(result, completeMacroSubString);
764     }
765   }
766
767   public static void createExternalLink(StringBuffer result, RenderEngine wikiEngine, String urlString) {
768
769     // Does our engine know images?
770     if (wikiEngine instanceof ImageRenderEngine) {
771       result.append(((ImageRenderEngine) wikiEngine).getExternalImageLink());
772     }
773     result.append("<span class=\"nobr\">");
774     result.append("<a href=\"");
775     result.append(Encoder.escape(urlString));
776     result.append("\">");
777     result.append(Encoder.toEntity(urlString.charAt(0)) + urlString.substring(1));
778     result.append("</a></span>");
779   }
780
781   public static void handleSnipLink(StringBuffer result, RenderEngine wikiEngine, String name) {
782     if (name != null) {
783       int index = name.indexOf("http://");
784       // Configuration probably wrote [http://radeox.org] instead of http://radeox.org
785       if (index != -1) {
786         createExternalLink(result, wikiEngine, name.substring(index));
787         // show error
788         // fResult.append("<div class=\"error\">Do not surround URLs with [...].</div>");
789       } else {
790         // trim the name and unescape it
791         name = Encoder.unescape(name.trim());
792         //                Is there an alias like [alias|link] ?
793         int pipeIndex = name.indexOf('|');
794         String alias = "";
795         if (-1 != pipeIndex) {
796           alias = name.substring(0, pipeIndex);
797           name = name.substring(pipeIndex + 1);
798         }
799
800         int hashIndex = name.lastIndexOf('#');
801
802         String hash = "";
803         if (-1 != hashIndex && hashIndex != name.length() - 1) {
804           hash = name.substring(hashIndex + 1);
805           name = name.substring(0, hashIndex);
806         }
807
808         int colonIndex = name.indexOf(':');
809         // typed link ?
810         if (-1 != colonIndex) {
811           // for now throw away the fType information
812           name = name.substring(colonIndex + 1);
813         }
814
815         int atIndex = name.lastIndexOf('@');
816         // InterWiki link ?
817         if (-1 != atIndex) {
818           String extSpace = name.substring(atIndex + 1);
819           // known extarnal space ?
820           InterWiki interWiki = InterWiki.getInstance();
821           if (interWiki.contains(extSpace)) {
822             name = name.substring(0, atIndex);
823             Writer writer = new StringBufferWriter(result);
824             try {
825               if (-1 != hashIndex) {
826                 interWiki.expand(writer, extSpace, name, hash);
827               } else {
828                 interWiki.expand(writer, extSpace, name, "");
829               }
830             } catch (IOException e) {
831 //              log.debug("InterWiki " + extSpace + " not found.");
832             }
833           } else {
834             result.append("&#91;<span class=\"error\">");
835             result.append(name);
836             result.append("?</span>&#93;");
837           }
838         } else {
839           // internal link
840           if (wikiEngine != null && wikiEngine instanceof WikiRenderEngine) {
841             if (((WikiRenderEngine) wikiEngine).exists(name)) {
842               String view = name;
843               if (-1 != pipeIndex) {
844                 view = alias;
845               }
846               // Do not add hash if an alias was given
847               if (-1 != hashIndex) {
848                 ((WikiRenderEngine) wikiEngine).appendLink(result, name, view, hash);
849               } else {
850                 ((WikiRenderEngine) wikiEngine).appendLink(result, name, view);
851               }
852             } else if (((WikiRenderEngine) wikiEngine).showCreate()) {
853               ((WikiRenderEngine) wikiEngine).appendCreateLink(result, name, name);
854               // links with "create" are not cacheable because
855               // a missing wiki could be created
856               // TODO is this ok?
857               //  fContext.getRenderContext().setCacheable(false);
858             } else {
859               // cannot display/create wiki, so just display the text
860               result.append(name);
861             }
862           } else {
863             // cannot display/create wiki, so just display the text
864             result.append(name);
865           }
866         }
867       }
868     }
869   }
870
871   public static void handleWikipediaLink(StringBuffer result, RenderEngine wikiEngine, String name, String suffix) {
872     if (name != null) {
873       int index = name.indexOf("http://");
874       // Configuration probably wrote [http://radeox.org] instead of http://radeox.org
875       if (index != -1) {
876         // createExternalLink(result, wikiEngine, name.substring(index));
877         String urlString = name.substring(index);
878         // Wikipedia like style:
879         int pipeIndex = urlString.indexOf(' ');
880         String alias = "";
881         if (-1 != pipeIndex) {
882           alias = urlString.substring(pipeIndex + 1);
883           urlString = urlString.substring(0, pipeIndex);
884         } else {
885           alias = urlString;
886         }
887
888         if (wikiEngine instanceof ImageRenderEngine) {
889           result.append(((ImageRenderEngine) wikiEngine).getExternalImageLink());
890         }
891         result.append("<span class=\"nobr\">");
892         result.append("<a href=\"");
893         result.append(Encoder.escape(urlString));
894         result.append("\">");
895         result.append(Encoder.toEntity(alias.charAt(0)) + alias.substring(1));
896         result.append("</a></span>");
897       } else {
898         if (suffix == null) {
899           suffix = "";
900         }
901         // trim the name and unescape it
902         name = Encoder.unescape(name.trim());
903         
904         name = name.replaceAll(":","/");
905         
906         //                Is there an alias like [alias|link] ?
907         int pipeIndex = name.indexOf('|');
908         String alias = "";
909         if (-1 != pipeIndex) {
910           alias = name.substring(pipeIndex + 1);
911           name = name.substring(0, pipeIndex);
912         }
913
914         int hashIndex = name.lastIndexOf('#');
915
916         String hash = "";
917         if (-1 != hashIndex && hashIndex != name.length() - 1) {
918           hash = name.substring(hashIndex + 1);
919           name = name.substring(0, hashIndex);
920         }
921
922         int colonIndex = name.indexOf(':');
923         // typed link ?
924         if (-1 != colonIndex) {
925           // for now throw away the fType information
926           name = name.substring(colonIndex + 1);
927         }
928
929         int atIndex = name.lastIndexOf('@');
930         // InterWiki link ?
931         if (-1 != atIndex) {
932           String extSpace = name.substring(atIndex + 1);
933           // known external space ?
934           InterWiki interWiki = InterWiki.getInstance();
935           if (interWiki.contains(extSpace)) {
936             name = name.substring(0, atIndex);
937             Writer writer = new StringBufferWriter(result);
938             try {
939               if (-1 != hashIndex) {
940                 interWiki.expand(writer, extSpace, name, hash);
941               } else {
942                 interWiki.expand(writer, extSpace, name, "");
943               }
944             } catch (IOException e) {
945 //              log.debug("InterWiki " + extSpace + " not found.");
946             }
947           } else {
948             result.append("&#91;<span class=\"error\">");
949             result.append(name);
950             result.append("?</span>&#93;");
951           }
952         } else {
953           // internal link
954           if (wikiEngine != null && wikiEngine instanceof WikiRenderEngine) {
955             if (((WikiRenderEngine) wikiEngine).exists(name)) {
956               String view = name + suffix;
957               if (-1 != pipeIndex) {
958                 view = alias + suffix;
959               }
960               // Do not add hash if an alias was given
961               if (-1 != hashIndex) {
962                 ((WikiRenderEngine) wikiEngine).appendLink(result, name, view, hash);
963               } else {
964                 ((WikiRenderEngine) wikiEngine).appendLink(result, name, view);
965               }
966             } else if (((WikiRenderEngine) wikiEngine).showCreate()) {
967               ((WikiRenderEngine) wikiEngine).appendCreateLink(result, name, name + suffix);
968               // links with "create" are not cacheable because
969               // a missing wiki could be created
970               // TODO is this ok?
971               //  fContext.getRenderContext().setCacheable(false);
972             } else {
973               // cannot display/create wiki, so just display the text
974               result.append(name);
975             }
976           } else {
977             // cannot display/create wiki, so just display the text
978             result.append(name);
979           }
980         }
981       }
982     }
983   }
984 }