1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package fr.paris.lutece.plugins.digglike.utils;
35
36 import java.io.Serializable;
37
38 import java.util.Collections;
39 import java.util.Comparator;
40 import java.util.HashSet;
41 import java.util.LinkedList;
42 import java.util.Set;
43 import java.util.Stack;
44 import java.util.regex.Matcher;
45 import java.util.regex.Pattern;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 public class ProcessBBCode implements Serializable
72 {
73 private static final long serialVersionUID = 2923413457835274235L;
74 private static final String CR_LF = "(?:\r\n|\r|\n)?";
75
76
77 private boolean acceptHTML = false;
78
79
80 private boolean acceptBBCode = true;
81
82
83
84
85 public boolean getAcceptBBCode( )
86 {
87 return acceptBBCode;
88 }
89
90
91
92
93 public void setAcceptBBCode( boolean acceptBBCode )
94 {
95 this.acceptBBCode = acceptBBCode;
96 }
97
98
99
100
101 public boolean getAcceptHTML( )
102 {
103 return acceptHTML;
104 }
105
106
107
108
109 public void setAcceptHTML( boolean acceptHTML )
110 {
111 this.acceptHTML = acceptHTML;
112 }
113
114
115
116
117
118 public String preparePostText( String texto )
119 {
120 if ( !getAcceptHTML( ) )
121 {
122 texto = escapeHtml( texto );
123 }
124
125 if ( getAcceptBBCode( ) )
126 {
127 texto = process( texto );
128 }
129
130 return texto;
131 }
132
133
134
135
136
137 private String process( String string )
138 {
139 string = string.replaceAll( "(\r\n|\n\r|\n|\r)", "<br>" );
140
141 StringBuffer buffer = new StringBuffer( string );
142 processCode( buffer );
143
144 processNestedTags( buffer, "quote",
145 "<table width='90%' cellspacing='1' cellpadding='1' border='0' " +
146 "align='center'><tr><td><span class='genmed'><b>{BBCODE_PARAM}:</b></span></td></tr></table><table width='90%' cellspacing='1' cellpadding='1' border='0' align='center'><tr><td class='quote' bgcolor='#F3F5FF'>",
147 " </td></tr></table>",
148 "<table width='90%' cellspacing='1' cellpadding='3' border='0' " +
149 "align='center'><tr><td><span class='genmed'><b>Quote:</b></span></td></tr><tr><td class='quote'>",
150 " </td></tr></table>", "[*]", false, true, true );
151
152 processNestedTags( buffer, "list", "<ol type=\"{BBCODE_PARAM}\">", "</ol>", "<ul>", "</ul>", "<li>", true,
153 true, false );
154
155 String str = buffer.toString( );
156
157
158
159
160 str = str.replaceAll( "\\[color=['\"]?(.*?[^'\"])['\"]?\\](.*?)\\[/color\\]", "<span style='color:$1'>$2</span>" );
161
162
163 str = str.replaceAll( "\\[size=['\"]?([0-9]|[1-5][0-9])['\"]?\\](.*?)\\[/size\\]",
164 "<span style='font-size:$1px'>$2</span>" );
165
166
167 str = str.replaceAll( "\\[font=['\"]?(.*?[^'\"])['\"]?\\](.*?)\\[/font\\]", "<font face='$1'>$2</font>" );
168
169
170 str = str.replaceAll( "\\[b\\](.*?)\\[/b\\]", "<b>$1</b>" );
171 str = str.replaceAll( "\\[u\\](.*?)\\[/u\\]", "<u>$1</u>" );
172 str = str.replaceAll( "\\[i\\](.*?)\\[/i\\]", "<i>$1</i>" );
173
174
175 str = str.replaceAll( "\\[img\\](.*?)\\[/img\\]", "<img src='$1' border='0' alt='0'>" );
176
177
178 str = str.replaceAll( "\\[url\\](.*?)\\[/url\\]", "<a href='$1' target='_blank'>$1</a>" );
179 str = str.replaceAll( "\\[url=['\"]?(.*?[^'\"])['\"]?\\](.*?)\\[/url\\]",
180 "<a href=\"$1\" target=\"_new\">$2</a>" );
181
182
183 str = str.replaceAll( "\\[email\\](.*?)\\[/email\\]", "<a href='mailto:$1'>$1</a>" );
184
185 return str;
186 }
187
188 private static void processCode( StringBuffer buffer )
189 {
190 int start = buffer.indexOf( "[code]" );
191 int end;
192
193 for ( ; ( start >= 0 ) && ( start < buffer.length( ) ); start = buffer.indexOf( "[code]", end ) )
194 {
195 end = buffer.indexOf( "[/code]", start );
196
197 if ( end < 0 )
198 {
199 break;
200 }
201
202 end += "[/code]".length( );
203
204 String content = buffer.substring( start + "[code]".length( ), end - "[/code]".length( ) );
205 content = escapeBBcode( content );
206
207
208
209
210
211
212
213
214
215
216
217 content = content.replaceAll( "<br>", "\n" );
218
219 String replacement = "<!-- [ -code- ] --></span>"
220 +"<textarea name=\"code\" id=\"code\" class=\"java\" rows=\"15\" cols=\"100\">" + content +
221 "</textarea><span class='postbody'><!-- [/ -code- ] -->";
222
223 buffer.replace( start, end, replacement );
224
225 end = start + replacement.length( );
226 }
227 }
228
229
230
231
232
233 public static String escapeBBcode( String content )
234 {
235
236 content = replaceAll( content, "[]\t".toCharArray( ), new String[] { "[", "]", " " } );
237
238
239 content = content.replaceAll( "\\A\r\n|\\A\r|\\A\n|\r\n\\z|\r\\z|\n\\z", "" );
240
241
242 content = content.replaceAll( " ", " " );
243 content = content.replaceAll( " ", " " );
244
245 return content;
246 }
247
248
249
250
251
252 private static String escapeHtml( String content )
253 {
254
255 content = replaceAll( content, "&<>".toCharArray( ), new String[] { "&", "<", ">" } );
256
257
258
259
260 return content;
261 }
262
263 private static String replaceAll( String str, char[] chars, String[] replacement )
264 {
265 StringBuffer buffer = new StringBuffer( );
266
267 for ( int i = 0; i < str.length( ); i++ )
268 {
269 char c = str.charAt( i );
270 boolean matched = false;
271
272 for ( int j = 0; j < chars.length; j++ )
273 {
274 if ( c == chars[j] )
275 {
276 buffer.append( replacement[j] );
277 matched = true;
278 }
279 }
280
281 if ( !matched )
282 {
283 buffer.append( c );
284 }
285 }
286
287 return buffer.toString( );
288 }
289
290
291
292
293
294
295
296
297
298
299
300
301
302 private static void processNestedTags( StringBuffer buffer, String tagName, String openSubstWithParam,
303 String closeSubstWithParam, String openSubstWithoutParam, String closeSubstWithoutParam, String internalSubst,
304 boolean processInternalTags, boolean acceptParam, boolean requiresQuotedParam )
305 {
306 String str = buffer.toString( );
307
308 Stack<MutableCharSequence> openStack = new Stack<MutableCharSequence>( );
309 Set<MutableCharSequence> subsOpen = new HashSet<MutableCharSequence>( );
310 Set<MutableCharSequence> subsClose = new HashSet<MutableCharSequence>( );
311 Set<MutableCharSequence> subsInternal = new HashSet<MutableCharSequence>( );
312
313 String openTag = CR_LF + "\\[" + tagName +
314 ( acceptParam ? ( requiresQuotedParam ? "(?:=\"(.*?)\")?" : "(?:=\"?(.*?)\"?)?" ) : "" ) + "\\]" + CR_LF;
315 String closeTag = CR_LF + "\\[/" + tagName + "\\]" + CR_LF;
316 String internTag = CR_LF + "\\[\\*\\]" + CR_LF;
317
318 String patternString = "(" + openTag + ")|(" + closeTag + ")";
319
320 if ( processInternalTags )
321 {
322 patternString += ( "|(" + internTag + ")" );
323 }
324
325 Pattern tagsPattern = Pattern.compile( patternString );
326 Matcher matcher = tagsPattern.matcher( str );
327
328 int openTagGroup;
329 int paramGroup;
330 int closeTagGroup;
331 int internalTagGroup;
332
333 if ( acceptParam )
334 {
335 openTagGroup = 1;
336 paramGroup = 2;
337 closeTagGroup = 3;
338 internalTagGroup = 4;
339 }
340 else
341 {
342 openTagGroup = 1;
343 paramGroup = -1;
344 closeTagGroup = 2;
345 internalTagGroup = 3;
346 }
347
348 while ( matcher.find( ) )
349 {
350 int length = matcher.end( ) - matcher.start( );
351 MutableCharSequence matchedSeq = new MutableCharSequence( str, matcher.start( ), length );
352
353
354 if ( matcher.group( openTagGroup ) != null )
355 {
356 if ( acceptParam && ( matcher.group( paramGroup ) != null ) )
357 {
358 matchedSeq.param = matcher.group( paramGroup );
359 }
360
361 openStack.push( matchedSeq );
362
363
364 }
365 else if ( ( matcher.group( closeTagGroup ) != null ) && !openStack.isEmpty( ) )
366 {
367 MutableCharSequence openSeq = openStack.pop( );
368
369 if ( acceptParam )
370 {
371 matchedSeq.param = openSeq.param;
372 }
373
374 subsOpen.add( openSeq );
375 subsClose.add( matchedSeq );
376
377
378 }
379 else if ( processInternalTags && ( matcher.group( internalTagGroup ) != null ) && ( !openStack.isEmpty( ) ) )
380 {
381 subsInternal.add( matchedSeq );
382 }
383 else
384 {
385
386 }
387 }
388
389 LinkedList<MutableCharSequence> subst = new LinkedList<MutableCharSequence>( );
390 subst.addAll( subsOpen );
391 subst.addAll( subsClose );
392 subst.addAll( subsInternal );
393
394 Collections.sort( subst,
395 new Comparator<MutableCharSequence>( )
396 {
397 public int compare( MutableCharSequence o1, MutableCharSequence o2 )
398 {
399 return -( o1.start - o2.start );
400 }
401 } );
402
403 buffer.delete( 0, buffer.length( ) );
404
405 int start = 0;
406
407 while ( !subst.isEmpty( ) )
408 {
409 MutableCharSequence seq = subst.removeLast( );
410 buffer.append( str.substring( start, seq.start ) );
411
412 if ( subsClose.contains( seq ) )
413 {
414 if ( seq.param != null )
415 {
416 buffer.append( closeSubstWithParam );
417 }
418 else
419 {
420 buffer.append( closeSubstWithoutParam );
421 }
422 }
423 else if ( subsInternal.contains( seq ) )
424 {
425 buffer.append( internalSubst );
426 }
427 else if ( subsOpen.contains( seq ) )
428 {
429 Matcher m = Pattern.compile( openTag ).matcher( str.substring( seq.start, seq.start + seq.length ) );
430
431 if ( m.matches( ) )
432 {
433 if ( acceptParam && ( seq.param != null ) )
434 {
435 buffer.append(
436 openSubstWithParam.replaceAll( "\\{BBCODE_PARAM\\}", seq.param ) );
437 }
438 else
439 {
440 buffer.append( openSubstWithoutParam );
441 }
442 }
443 }
444
445 start = seq.start + seq.length;
446 }
447
448 buffer.append( str.substring( start ) );
449 }
450
451 static class MutableCharSequence implements CharSequence
452 {
453
454 public CharSequence base;
455
456
457 public int start;
458
459
460 public int length;
461
462
463 public String param = null;
464
465
466
467 public MutableCharSequence( )
468 {
469
470 }
471
472
473
474
475
476
477 public MutableCharSequence( CharSequence base, int start, int length )
478 {
479 reset( base, start, length );
480 }
481
482
483
484
485 public int length( )
486 {
487 return this.length;
488 }
489
490
491
492
493 public char charAt( int index )
494 {
495 return this.base.charAt( this.start + index );
496 }
497
498
499
500
501 public CharSequence subSequence( int pStart, int end )
502 {
503 return new MutableCharSequence( this.base, this.start + pStart, this.start + ( end - pStart ) );
504 }
505
506
507
508
509
510
511
512 public CharSequence reset( CharSequence pBase, int pStart, int pLength )
513 {
514 this.base = pBase;
515 this.start = pStart;
516 this.length = pLength;
517
518 return this;
519 }
520
521
522
523
524 public String toString( )
525 {
526 StringBuffer sb = new StringBuffer( );
527
528 for ( int i = this.start; i < ( this.start + this.length ); i++ )
529 {
530 sb.append( this.base.charAt( i ) );
531 }
532
533 return sb.toString( );
534 }
535 }
536 }