Mercurial Hosting > luan
comparison src/goodjava/bbcode/BBCode.java @ 1737:6c9aea554691
generalize bbcode
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Tue, 18 Oct 2022 22:08:29 -0600 |
parents | 31a82b0d0a87 |
children |
comparison
equal
deleted
inserted
replaced
1736:a02a75e3daa8 | 1737:6c9aea554691 |
---|---|
9 | 9 |
10 | 10 |
11 public final class BBCode { | 11 public final class BBCode { |
12 | 12 |
13 private static final Pattern tagPtn = Pattern.compile( | 13 private static final Pattern tagPtn = Pattern.compile( |
14 "\\[(/?[a-zA-Z]+(=[^ \\n\\t\\[\\]]*)?)\\]" | 14 "\\[(/?[a-zA-Z_]+(=[^ \\n\\t\\[\\]]*)?)\\]" |
15 ); | 15 ); |
16 | 16 |
17 public static String encode(String s) { | 17 public static String encode(String s) { |
18 return tagPtn.matcher(s).replaceAll("[brackets]$1[/brackets]"); | 18 return tagPtn.matcher(s).replaceAll("[brackets]$1[/brackets]"); |
19 } | 19 } |
21 | 21 |
22 public final class Element { | 22 public final class Element { |
23 public final String name; | 23 public final String name; |
24 public final String param; | 24 public final String param; |
25 public final Object contents; // String, Element, or List | 25 public final Object contents; // String, Element, or List |
26 public final Map<String,String> extra; | |
27 | |
28 private Element(String name,Object contents) { | |
29 this(name,null,contents); | |
30 } | |
31 | 26 |
32 private Element(String name,String param,Object contents) { | 27 private Element(String name,String param,Object contents) { |
33 this(name,param,contents,null); | |
34 } | |
35 | |
36 private Element(String name,String param,Object contents,Map<String,String> extra) { | |
37 this.name = name.toLowerCase(); | 28 this.name = name.toLowerCase(); |
38 this.param = param; | 29 this.param = param; |
39 this.contents = contents; | 30 this.contents = contents; |
40 this.extra = extra; | |
41 } | 31 } |
42 } | 32 } |
43 | 33 |
44 public static Object parse(String text) { | 34 public static Object parse(String text) { |
45 if( text.indexOf('[') == -1 ) | 35 if( text.indexOf('[') == -1 ) |
55 | 45 |
56 private Object parse() { | 46 private Object parse() { |
57 List list = new ArrayList(); | 47 List list = new ArrayList(); |
58 StringBuilder text = new StringBuilder(); | 48 StringBuilder text = new StringBuilder(); |
59 while( !parser.endOfInput() ) { | 49 while( !parser.endOfInput() ) { |
60 Element block = parseBlock(false); | 50 Element block = parseBlock(); |
61 if( block != null ) { | 51 if( block != null ) { |
62 add(list,text); | 52 add(list,text); |
63 list.add( block ); | 53 list.add( block ); |
64 } else { | 54 } else { |
65 text.append( parser.currentChar() ); | 55 text.append( parser.currentChar() ); |
69 add(list,text); | 59 add(list,text); |
70 return list.size()==1 ? list.get(0) : list; | 60 return list.size()==1 ? list.get(0) : list; |
71 } | 61 } |
72 | 62 |
73 private Object parseUntil(String end) { | 63 private Object parseUntil(String end) { |
74 return parseUntil(end,false); | |
75 } | |
76 | |
77 private Object parseUntil(String end,boolean inList) { | |
78 List list = new ArrayList(); | 64 List list = new ArrayList(); |
79 StringBuilder text = new StringBuilder(); | 65 StringBuilder text = new StringBuilder(); |
80 while( !parser.matchIgnoreCase(end) ) { | 66 while( !parser.matchIgnoreCase(end) ) { |
81 if( parser.endOfInput() ) | 67 if( parser.endOfInput() ) |
82 return null; | 68 return null; |
83 Element block = parseBlock(inList); | 69 Element block = parseBlock(); |
84 if( block != null ) { | 70 if( block != null ) { |
85 add(list,text); | 71 add(list,text); |
86 list.add( block ); | 72 list.add( block ); |
87 } else { | 73 } else { |
88 text.append( parser.currentChar() ); | 74 text.append( parser.currentChar() ); |
98 list.add( text.toString() ); | 84 list.add( text.toString() ); |
99 text.setLength(0); | 85 text.setLength(0); |
100 } | 86 } |
101 } | 87 } |
102 | 88 |
103 private Element parseBlock(boolean inList) { | 89 private Element parseBlock() { |
104 if( parser.currentChar() != '[' ) | 90 if( parser.currentChar() != '[' ) |
105 return null; | 91 return null; |
106 Element s; | 92 parser.begin(); |
107 s = parseB(); if(s!=null) return s; | 93 parser.anyChar(); // [ |
108 s = parseI(); if(s!=null) return s; | 94 int start = parser.currentIndex(); |
109 s = parseU(); if(s!=null) return s; | 95 if( !matchTagChar() ) |
110 s = parseS(); if(s!=null) return s; | 96 return parser.failure(null); |
111 s = parseSup(); if(s!=null) return s; | 97 while(matchTagChar()); |
112 s = parseBrackets(); if(s!=null) return s; | 98 String name = parser.textFrom(start).toLowerCase(); |
113 s = parseUrl(); if(s!=null) return s; | 99 String param = null; |
114 s = parseCode(); if(s!=null) return s; | 100 if( parser.match('=') ) { |
115 s = parseImg(); if(s!=null) return s; | 101 start = parser.currentIndex(); |
116 s = parseColor(); if(s!=null) return s; | 102 while( parser.noneOf("[]\n") ); |
117 s = parseSize(); if(s!=null) return s; | 103 param = parser.textFrom(start); |
118 s = parseVideo(); if(s!=null) return s; | |
119 s = parseQuote(); if(s!=null) return s; | |
120 s = parseUl(); if(s!=null) return s; | |
121 s = parseOl(); if(s!=null) return s; | |
122 if( inList ) { | |
123 s = parseLi(); if(s!=null) return s; | |
124 } | 104 } |
125 return null; | 105 if( !parser.match(']') ) |
126 } | |
127 | |
128 private Element parseB() { | |
129 parser.begin(); | |
130 if( !parser.matchIgnoreCase("[b]") ) | |
131 return parser.failure(null); | 106 return parser.failure(null); |
132 Object content = parseUntil("[/b]"); | 107 Object content = parseUntil("[/"+name+"]"); |
133 if( content==null ) | 108 if( content==null ) |
134 return parser.failure(null); | 109 return parser.failure(null); |
135 Element rtn = new Element("b",content); | 110 Element rtn = new Element(name,param,content); |
136 return parser.success(rtn); | 111 return parser.success(rtn); |
137 } | 112 } |
138 | 113 |
139 private Element parseI() { | 114 private boolean matchTagChar() { |
140 parser.begin(); | 115 return parser.inCharRange('a','z') |
141 if( !parser.matchIgnoreCase("[i]") ) | |
142 return parser.failure(null); | |
143 Object content = parseUntil("[/i]"); | |
144 if( content==null ) | |
145 return parser.failure(null); | |
146 Element rtn = new Element("i",content); | |
147 return parser.success(rtn); | |
148 } | |
149 | |
150 private Element parseU() { | |
151 parser.begin(); | |
152 if( !parser.matchIgnoreCase("[u]") ) | |
153 return parser.failure(null); | |
154 Object content = parseUntil("[/u]"); | |
155 if( content==null ) | |
156 return parser.failure(null); | |
157 Element rtn = new Element("u",content); | |
158 return parser.success(rtn); | |
159 } | |
160 | |
161 private Element parseS() { | |
162 parser.begin(); | |
163 if( !parser.matchIgnoreCase("[s]") ) | |
164 return parser.failure(null); | |
165 Object content = parseUntil("[/s]"); | |
166 if( content==null ) | |
167 return parser.failure(null); | |
168 Element rtn = new Element("s",content); | |
169 return parser.success(rtn); | |
170 } | |
171 | |
172 private Element parseSup() { | |
173 parser.begin(); | |
174 if( !parser.matchIgnoreCase("[sup]") ) | |
175 return parser.failure(null); | |
176 Object content = parseUntil("[/sup]"); | |
177 if( content==null ) | |
178 return parser.failure(null); | |
179 Element rtn = new Element("sup",content); | |
180 return parser.success(rtn); | |
181 } | |
182 | |
183 private Element parseBrackets() { | |
184 parser.begin(); | |
185 if( !parser.matchIgnoreCase("[brackets]") ) | |
186 return parser.failure(null); | |
187 Object content = parseUntil("[/brackets]"); | |
188 if( content==null ) | |
189 return parser.failure(null); | |
190 Element rtn = new Element("brackets",content); | |
191 return parser.success(rtn); | |
192 } | |
193 | |
194 private Element parseUrl() { | |
195 parser.begin(); | |
196 if( !parser.matchIgnoreCase("[url") ) | |
197 return parser.failure(null); | |
198 String url; | |
199 Object content; | |
200 if( parser.match(']') ) { | |
201 url = parseRealUrl(); | |
202 content = null; | |
203 if( !parser.matchIgnoreCase("[/url]") ) | |
204 return parser.failure(null); | |
205 } else if( parser.match('=') ) { | |
206 url = parseRealUrl(); | |
207 if( !parser.match(']') ) | |
208 return parser.failure(null); | |
209 content = parseUntil("[/url]"); | |
210 if( content==null ) | |
211 return parser.failure(null); | |
212 } else | |
213 return parser.failure(null); | |
214 Element rtn = new Element("url",url,content); | |
215 return parser.success(rtn); | |
216 } | |
217 | |
218 private String parseRealUrl() { | |
219 parser.begin(); | |
220 while( parser.match(' ') ); | |
221 int start = parser.currentIndex(); | |
222 if( !parser.matchIgnoreCase("http") ) | |
223 return parser.failure(null); | |
224 parser.matchIgnoreCase("s"); | |
225 if( !parser.matchIgnoreCase("://") ) | |
226 return parser.failure(null); | |
227 while( parser.noneOf(" \n\t[]") ); | |
228 String url = parser.textFrom(start); | |
229 while( parser.match(' ') ); | |
230 return parser.success(url); | |
231 } | |
232 | |
233 private Element parseCode() { | |
234 parser.begin(); | |
235 String param; | |
236 String end; | |
237 if( !parser.matchIgnoreCase("[code") ) | |
238 return parser.failure(null); | |
239 if( parser.match(']') ) { | |
240 param = null; | |
241 end = "[/code]"; | |
242 } else if( parser.match('=') ) { | |
243 int start = parser.currentIndex(); | |
244 while( parser.noneOf("[]\n") ); | |
245 param = parser.textFrom(start); | |
246 if( !parser.match(']') ) | |
247 return parser.failure(null); | |
248 end = "[/code=" + param + "]"; | |
249 } else | |
250 return parser.failure(null); | |
251 int start = parser.currentIndex(); | |
252 while( !parser.testIgnoreCase(end) ) { | |
253 if( !parser.anyChar() ) | |
254 return parser.failure(null); | |
255 } | |
256 String content = parser.textFrom(start); | |
257 if( !parser.matchIgnoreCase(end) ) throw new RuntimeException(); | |
258 Element rtn = new Element("code",param,content); | |
259 return parser.success(rtn); | |
260 } | |
261 | |
262 private Element parseImg() { | |
263 parser.begin(); | |
264 if( !parser.matchIgnoreCase("[img]") ) | |
265 return parser.failure(null); | |
266 String url = parseRealUrl(); | |
267 if( !parser.matchIgnoreCase("[/img]") ) | |
268 return parser.failure(null); | |
269 Element rtn = new Element("img",url); | |
270 return parser.success(rtn); | |
271 } | |
272 | |
273 private Element parseColor() { | |
274 parser.begin(); | |
275 if( !parser.matchIgnoreCase("[color=") ) | |
276 return parser.failure(null); | |
277 int start = parser.currentIndex(); | |
278 while( parser.inCharRange('0','9') | |
279 || parser.inCharRange('a','z') | |
280 || parser.inCharRange('A','Z') | 116 || parser.inCharRange('A','Z') |
281 || parser.anyOf("#(, )") | 117 || parser.inCharRange('0','9') |
282 ); | 118 || parser.match('_') |
283 String color = parser.textFrom(start); | 119 ; |
284 if( !parser.match(']') ) | |
285 return parser.failure(null); | |
286 Object content = parseUntil("[/color]"); | |
287 if( content==null ) | |
288 return parser.failure(null); | |
289 Element rtn = new Element("color",color,content); | |
290 return parser.success(rtn); | |
291 } | |
292 | |
293 private Element parseSize() { | |
294 parser.begin(); | |
295 if( !parser.matchIgnoreCase("[size=") ) | |
296 return parser.failure(null); | |
297 int start = parser.currentIndex(); | |
298 while( parser.match('.') || parser.inCharRange('0','9') ); | |
299 if( parser.matchIgnoreCase("pt") || parser.match('%') ) | |
300 ; // ok | |
301 String size = parser.textFrom(start); | |
302 if( !parser.match(']') ) | |
303 return parser.failure(null); | |
304 Object content = parseUntil("[/size]"); | |
305 if( content==null ) | |
306 return parser.failure(null); | |
307 Element rtn = new Element("size",size,content); | |
308 return parser.success(rtn); | |
309 } | |
310 | |
311 private Element parseVideo() { | |
312 parser.begin(); | |
313 if( !parser.matchIgnoreCase("[video]") ) | |
314 return parser.failure(null); | |
315 String url = parseRealUrl(); | |
316 if( !parser.matchIgnoreCase("[/video]") ) | |
317 return parser.failure(null); | |
318 Element rtn = new Element("video",null,url); | |
319 return parser.success(rtn); | |
320 } | |
321 | |
322 | |
323 private Element parseQuote() { | |
324 parser.begin(); | |
325 if( !parser.matchIgnoreCase("[quote") ) | |
326 return parser.failure(null); | |
327 String name; | |
328 if( parser.match(']') ) { | |
329 name = null; | |
330 } else if( parser.match('=') ) { | |
331 int start = parser.currentIndex(); | |
332 while( parser.noneOf("[]\n") ); | |
333 name = parser.textFrom(start); | |
334 if( !parser.match(']') ) | |
335 return parser.failure(null); | |
336 } else | |
337 return parser.failure(null); | |
338 Object content = parseUntil("[/quote]"); | |
339 if( content==null ) | |
340 return parser.failure(null); | |
341 Element rtn = new Element("quote",name,content); | |
342 return parser.success(rtn); | |
343 } | |
344 | |
345 | |
346 private Element parseUl() { | |
347 parser.begin(); | |
348 if( !parser.matchIgnoreCase("[ul]") ) | |
349 return parser.failure(null); | |
350 Object content = parseUntil("[/ul]",true); | |
351 if( content==null ) | |
352 return parser.failure(null); | |
353 Element rtn = new Element("ul",content); | |
354 return parser.success(rtn); | |
355 } | |
356 | |
357 private Element parseOl() { | |
358 parser.begin(); | |
359 if( !parser.matchIgnoreCase("[ol]") ) | |
360 return parser.failure(null); | |
361 Object content = parseUntil("[/ol]",true); | |
362 if( content==null ) | |
363 return parser.failure(null); | |
364 Element rtn = new Element("ol",content); | |
365 return parser.success(rtn); | |
366 } | |
367 | |
368 private Element parseLi() { | |
369 parser.begin(); | |
370 if( !parser.matchIgnoreCase("[li]") ) | |
371 return parser.failure(null); | |
372 Object content = parseUntil("[/li]"); | |
373 if( content==null ) | |
374 return parser.failure(null); | |
375 Element rtn = new Element("li",content); | |
376 return parser.success(rtn); | |
377 } | 120 } |
378 | 121 |
379 } | 122 } |