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