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 }