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.util.parser;
35
36 import fr.paris.lutece.portal.business.editor.ParserComplexElement;
37 import fr.paris.lutece.portal.business.editor.ParserElement;
38
39 import java.util.Collections;
40 import java.util.Comparator;
41 import java.util.HashSet;
42 import java.util.LinkedList;
43 import java.util.List;
44 import java.util.Set;
45 import java.util.Stack;
46 import java.util.regex.Matcher;
47 import java.util.regex.Pattern;
48
49
50
51
52
53
54
55 public final class BbcodeUtil
56 {
57 private static final String CR_LF = "(?:\r\n|\r|\n)?";
58
59
60
61
62 private BbcodeUtil( )
63 {
64 }
65
66
67
68
69
70
71
72
73 public static String parse( String value, List<ParserElement> listParserElement,
74 List<ParserComplexElement> listParserComplexElement )
75 {
76 StringBuffer buffer = new StringBuffer( value );
77
78
79 if ( listParserComplexElement != null )
80 {
81 for ( ParserComplexElement element : listParserComplexElement )
82 {
83 processNestedTags( buffer, element.getTagName( ), element.getOpenSubstWithParam( ),
84 element.getCloseSubstWithParam( ), element.getOpenSubstWithoutParam( ),
85 element.getCloseSubstWithoutParam( ), element.getInternalSubst( ),
86 element.isProcessInternalTags( ), element.isAcceptParam( ), element.isRequiresQuotedParam( ) );
87 }
88 }
89
90 String str = buffer.toString( );
91
92
93 if ( listParserElement != null )
94 {
95 for ( ParserElement element : listParserElement )
96 {
97 str = str.replaceAll( element.getCode( ), element.getValue( ) );
98 }
99 }
100
101 return str;
102 }
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118 private static void processNestedTags( StringBuffer buffer, String tagName, String openSubstWithParam,
119 String closeSubstWithParam, String openSubstWithoutParam, String closeSubstWithoutParam, String internalSubst,
120 boolean processInternalTags, boolean acceptParam, boolean requiresQuotedParam )
121 {
122 String str = buffer.toString( );
123
124 Stack openStack = new Stack( );
125 Set subsOpen = new HashSet( );
126 Set subsClose = new HashSet( );
127 Set subsInternal = new HashSet( );
128
129 String openTag = CR_LF + "\\[" + tagName +
130 ( acceptParam ? ( requiresQuotedParam ? "(?:=\"(.*?)\")?" : "(?:=\"?(.*?)\"?)?" ) : "" ) + "\\]" + CR_LF;
131 String closeTag = CR_LF + "\\[/" + tagName + "\\]" + CR_LF;
132 String internTag = CR_LF + "\\[\\*\\]" + CR_LF;
133
134 String patternString = "(" + openTag + ")|(" + closeTag + ")";
135
136 if ( processInternalTags )
137 {
138 patternString += ( "|(" + internTag + ")" );
139 }
140
141 Pattern tagsPattern = Pattern.compile( patternString );
142 Matcher matcher = tagsPattern.matcher( str );
143
144 int openTagGroup;
145 int paramGroup;
146 int closeTagGroup;
147 int internalTagGroup;
148
149 if ( acceptParam )
150 {
151 openTagGroup = 1;
152 paramGroup = 2;
153 closeTagGroup = 3;
154 internalTagGroup = 4;
155 }
156 else
157 {
158 openTagGroup = 1;
159 paramGroup = -1;
160 closeTagGroup = 2;
161 internalTagGroup = 3;
162 }
163
164 while ( matcher.find( ) )
165 {
166 int length = matcher.end( ) - matcher.start( );
167 MutableCharSequence matchedSeq = new MutableCharSequence( str, matcher.start( ), length );
168
169
170 if ( matcher.group( openTagGroup ) != null )
171 {
172 if ( acceptParam && ( matcher.group( paramGroup ) != null ) )
173 {
174 matchedSeq._strParam = matcher.group( paramGroup );
175 }
176
177 openStack.push( matchedSeq );
178
179
180 }
181 else if ( ( matcher.group( closeTagGroup ) != null ) && !openStack.isEmpty( ) )
182 {
183 MutableCharSequence openSeq = (MutableCharSequence) openStack.pop( );
184
185 if ( acceptParam )
186 {
187 matchedSeq._strParam = openSeq._strParam;
188 }
189
190 subsOpen.add( openSeq );
191 subsClose.add( matchedSeq );
192
193
194 }
195 else if ( processInternalTags && ( matcher.group( internalTagGroup ) != null ) && ( !openStack.isEmpty( ) ) )
196 {
197 subsInternal.add( matchedSeq );
198 }
199 }
200
201 LinkedList subst = new LinkedList( );
202 subst.addAll( subsOpen );
203 subst.addAll( subsClose );
204 subst.addAll( subsInternal );
205
206 Collections.sort( subst,
207 new Comparator( )
208 {
209 @Override
210 public int compare( Object o1, Object o2 )
211 {
212 MutableCharSequence s1 = (MutableCharSequence) o1;
213 MutableCharSequence s2 = (MutableCharSequence) o2;
214
215 return -( s1._nStart - s2._nStart );
216 }
217 } );
218
219 buffer.delete( 0, buffer.length( ) );
220
221 int start = 0;
222
223 while ( !subst.isEmpty( ) )
224 {
225 MutableCharSequence seq = (MutableCharSequence) subst.removeLast( );
226 buffer.append( str.substring( start, seq._nStart ) );
227
228 if ( subsClose.contains( seq ) )
229 {
230 if ( seq._strParam != null )
231 {
232 buffer.append( closeSubstWithParam );
233 }
234 else
235 {
236 buffer.append( closeSubstWithoutParam );
237 }
238 }
239 else if ( subsInternal.contains( seq ) )
240 {
241 buffer.append( internalSubst );
242 }
243 else if ( subsOpen.contains( seq ) )
244 {
245 Matcher m = Pattern.compile( openTag ).matcher( str.substring( seq._nStart, seq._nStart + seq._bLength ) );
246
247 if ( m.matches( ) )
248 {
249 if ( acceptParam && ( seq._strParam != null ) )
250 {
251 buffer.append(
252 openSubstWithParam.replaceAll( "\\{BBCODE_PARAM\\}", seq._strParam ) );
253 }
254 else
255 {
256 buffer.append( openSubstWithoutParam );
257 }
258 }
259 }
260
261 start = seq._nStart + seq._bLength;
262 }
263
264 buffer.append( str.substring( start ) );
265 }
266
267
268
269
270
271
272 static class MutableCharSequence implements CharSequence
273 {
274
275 private CharSequence _cBase;
276
277
278 private int _nStart;
279
280
281 private int _bLength;
282
283
284 private String _strParam;
285
286
287
288
289 public MutableCharSequence( )
290 {
291 }
292
293
294
295
296
297
298 public MutableCharSequence( CharSequence base, int start, int length )
299 {
300 reset( base, start, length );
301 }
302
303
304
305
306 @Override
307 public int length( )
308 {
309 return _bLength;
310 }
311
312
313
314
315
316 @Override
317 public char charAt( int index )
318 {
319 return _cBase.charAt( _nStart + index );
320 }
321
322
323
324
325
326
327 @Override
328 public CharSequence subSequence( int pStart, int end )
329 {
330 return new MutableCharSequence( _cBase, _nStart + pStart, _nStart + ( end - pStart ) );
331 }
332
333
334
335
336
337
338
339
340 public CharSequence reset( CharSequence pBase, int pStart, int pLength )
341 {
342 _cBase = pBase;
343 _nStart = pStart;
344 _bLength = pLength;
345
346 return this;
347 }
348
349
350
351
352
353 @Override
354 public String toString( )
355 {
356 StringBuffer sb = new StringBuffer( );
357
358 for ( int i = _nStart; i < ( _nStart + _bLength ); i++ )
359 {
360 sb.append( _cBase.charAt( i ) );
361 }
362
363 return sb.toString( );
364 }
365 }
366 }