1
14
15 package com.liferay.portlet.messageboards.util;
16
17 import com.liferay.portal.kernel.log.Log;
18 import com.liferay.portal.kernel.log.LogFactoryUtil;
19 import com.liferay.portal.kernel.util.GetterUtil;
20 import com.liferay.portal.kernel.util.HtmlUtil;
21 import com.liferay.portal.kernel.util.StringBundler;
22 import com.liferay.portal.kernel.util.StringPool;
23 import com.liferay.portal.kernel.util.StringUtil;
24 import com.liferay.portlet.messageboards.model.MBMessage;
25
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30
31
36 public class BBCodeUtil {
37
38 static Map<Integer, String> fontSizes = new HashMap<Integer, String>();
39
40 static Map<String, String> listStyles = new HashMap<String, String>();
41
42 static String[][] emoticons = {
43 {"angry.gif", ":angry:"},
44 {"bashful.gif", ":bashful:"},
45 {"big_grin.gif", ":grin:"},
46 {"blink.gif", ":blink:"},
47 {"blush.gif", ":*)"},
48 {"bored.gif", ":bored:"},
49 {"closed_eyes.gif", "-_-"},
50 {"cold.gif", ":cold:"},
51 {"cool.gif", "B)"},
52 {"darth_vader.gif", ":vader:"},
53 {"dry.gif", "<_<"},
54 {"exclamation.gif", ":what:"},
55 {"girl.gif", ":girl:"},
56 {"glare.gif", ">_>"},
57 {"happy.gif", ":)"},
58 {"huh.gif", ":huh:"},
59 {"in_love.gif", "<3"},
60 {"karate_kid.gif", ":kid:"},
61 {"kiss.gif", ":#"},
62 {"laugh.gif", ":lol:"},
63 {"mad.gif", ":mad:"},
64 {"mellow.gif", ":mellow:"},
65 {"ninja.gif", ":ph34r:"},
66 {"oh_my.gif", ":O"},
67 {"pac_man.gif", ":V"},
68 {"roll_eyes.gif", ":rolleyes:"},
69 {"sad.gif", ":("},
70 {"sleep.gif", ":sleep:"},
71 {"smile.gif", ":D"},
72 {"smug.gif", ":smug:"},
73 {"suspicious.gif", "8o"},
74 {"tongue.gif", ":P"},
75 {"unsure.gif", ":unsure:"},
76 {"wacko.gif", ":wacko:"},
77 {"wink.gif", ":wink:"},
78 {"wub.gif", ":wub:"}
79 };
80
81 static {
82 fontSizes.put(new Integer(1), "<span style='font-size: 0.7em;'>");
83 fontSizes.put(new Integer(2), "<span style='font-size: 0.8em;'>");
84 fontSizes.put(new Integer(3), "<span style='font-size: 0.9em;'>");
85 fontSizes.put(new Integer(4), "<span style='font-size: 1.0em;'>");
86 fontSizes.put(new Integer(5), "<span style='font-size: 1.1em;'>");
87 fontSizes.put(new Integer(6), "<span style='font-size: 1.3em;'>");
88 fontSizes.put(new Integer(7), "<span style='font-size: 1.5em;'>");
89
90 listStyles.put("1", "<ol style='list-style: decimal inside;'>");
91 listStyles.put("i", "<ol style='list-style: lower-roman inside;'>");
92 listStyles.put("I", "<ol style='list-style: upper-roman inside;'>");
93 listStyles.put("a", "<ol style='list-style: lower-alpha inside;'>");
94 listStyles.put("A", "<ol style='list-style: upper-alpha inside;'>");
95
96 for (int i = 0; i < emoticons.length; i++) {
97 String[] emoticon = emoticons[i];
98
99 String image = emoticon[0];
100 String code = emoticon[1];
101
102 emoticon[0] =
103 "<img alt='emoticon' src='@theme_images_path@/emoticons/" +
104 image + "' />";
105 emoticon[1] = HtmlUtil.escape(code);
106 }
107 }
108
109 public static final String[][] EMOTICONS = emoticons;
110
111 public static String getHTML(MBMessage message) {
112 String body = message.getBody();
113
114 try {
115 body = getHTML(body);
116 }
117 catch (Exception e) {
118 _log.error(
119 "Could not parse message " + message.getMessageId() + " " +
120 e.getMessage());
121 }
122
123 return body;
124 }
125
126 public static String getHTML(String bbcode) {
127 String html = HtmlUtil.escape(bbcode);
128
129 html = StringUtil.replace(html, _BBCODE_TAGS, _HTML_TAGS);
130
131 for (int i = 0; i < emoticons.length; i++) {
132 String[] emoticon = emoticons[i];
133
134 html = StringUtil.replace(html, emoticon[1], emoticon[0]);
135 }
136
137 BBCodeTag tag = null;
138
139 StringBundler sb = null;
140
141 while ((tag = getFirstTag(html, "code")) != null) {
142 String preTag = html.substring(0, tag.getStartPos());
143 String postTag = html.substring(tag.getEndPos());
144
145 String code = tag.getElement().replaceAll(
146 "\t", StringPool.FOUR_SPACES);
147 String[] lines = code.split("\\n");
148 int digits = String.valueOf(lines.length + 1).length();
149
150 sb = new StringBundler(preTag);
151
152 sb.append("<div class='code'>");
153
154 for (int i = 0; i < lines.length; i++) {
155 String index = String.valueOf(i + 1);
156 int ld = index.length();
157
158 sb.append("<span class='code-lines'>");
159
160 for (int j = 0; j < digits - ld; j++) {
161 sb.append(" ");
162 }
163
164 lines[i] = StringUtil.replace(lines[i], " ",
165 StringPool.NBSP + StringPool.SPACE + StringPool.NBSP);
166 lines[i] = StringUtil.replace(lines[i], " ",
167 StringPool.NBSP + StringPool.SPACE);
168
169 sb.append(index + "</span>");
170 sb.append(lines[i]);
171
172 if (index.length() < lines.length) {
173 sb.append("<br />");
174 }
175 }
176
177 sb.append("</div>");
178 sb.append(postTag);
179
180 html = sb.toString();
181 }
182
183 while ((tag = getFirstTag(html, "color")) != null) {
184 String preTag = html.substring(0, tag.getStartPos());
185 String postTag = html.substring(tag.getEndPos());
186
187 if (sb == null) {
188 sb = new StringBundler(preTag);
189 }
190 else {
191 sb.setIndex(0);
192
193 sb.append(preTag);
194 }
195
196 if (tag.hasParameter()) {
197 sb.append("<span style='color: ");
198 sb.append(tag.getParameter() + ";'>");
199 sb.append(tag.getElement() + "</span>");
200 }
201 else {
202 sb.append(tag.getElement());
203 }
204
205 sb.append(postTag);
206
207 html = sb.toString();
208 }
209
210 while ((tag = getFirstTag(html, "email")) != null) {
211 String preTag = html.substring(0, tag.getStartPos());
212 String postTag = html.substring(tag.getEndPos());
213
214 String mailto = GetterUtil.getString(
215 tag.getParameter(), tag.getElement().trim());
216
217 if (sb == null) {
218 sb = new StringBundler(preTag);
219 }
220 else {
221 sb.setIndex(0);
222
223 sb.append(preTag);
224 }
225
226 sb.append(preTag);
227 sb.append("<a href='mailto: ");
228 sb.append(mailto);
229 sb.append("'>");
230 sb.append(tag.getElement() + "</a>");
231 sb.append(postTag);
232
233 html = sb.toString();
234 }
235
236 while ((tag = getFirstTag(html, "font")) != null) {
237 String preTag = html.substring(0, tag.getStartPos());
238 String postTag = html.substring(tag.getEndPos());
239
240 if (sb == null) {
241 sb = new StringBundler(preTag);
242 }
243 else {
244 sb.setIndex(0);
245
246 sb.append(preTag);
247 }
248
249 if (tag.hasParameter()) {
250 sb.append("<span style='font-family: ");
251 sb.append(tag.getParameter() + ";'>");
252 sb.append(tag.getElement() + "</span>");
253 }
254 else {
255 sb.append(tag.getElement());
256 }
257
258 sb.append(postTag);
259
260 html = sb.toString();
261 }
262
263 while ((tag = getFirstTag(html, "img")) != null) {
264 String preTag = html.substring(0, tag.getStartPos());
265 String postTag = html.substring(tag.getEndPos());
266
267 if (sb == null) {
268 sb = new StringBundler(preTag);
269 }
270 else {
271 sb.setIndex(0);
272
273 sb.append(preTag);
274 }
275
276 sb.append("<img alt='' src='");
277 sb.append(tag.getElement().trim());
278 sb.append("' />");
279 sb.append(postTag);
280
281 html = sb.toString();
282 }
283
284 while ((tag = getFirstTag(html, "list")) != null) {
285 String preTag = html.substring(0, tag.getStartPos());
286 String postTag = html.substring(tag.getEndPos());
287
288 String[] items = _getListItems(tag.getElement());
289
290 if (sb == null) {
291 sb = new StringBundler(preTag);
292 }
293 else {
294 sb.setIndex(0);
295
296 sb.append(preTag);
297 }
298
299 if (tag.hasParameter() &&
300 listStyles.containsKey(tag.getParameter())) {
301
302 sb.append(listStyles.get(tag.getParameter()));
303
304 for (int i = 0; i < items.length; i++) {
305 if (items[i].trim().length() > 0) {
306 sb.append("<li>" + items[i].trim() + "</li>");
307 }
308 }
309
310 sb.append("</ol>");
311 }
312 else {
313 sb.append("<ul style='list-style: disc inside;'>");
314
315 for (int i = 0; i < items.length; i++) {
316 if (items[i].trim().length() > 0) {
317 sb.append("<li>" + items[i].trim() + "</li>");
318 }
319 }
320
321 sb.append("</ul>");
322 }
323
324 sb.append(postTag);
325
326 html = sb.toString();
327 }
328
329 while ((tag = getFirstTag(html, "quote")) != null) {
330 String preTag = html.substring(0, tag.getStartPos());
331 String postTag = html.substring(tag.getEndPos());
332
333 if (sb == null) {
334 sb = new StringBundler(preTag);
335 }
336 else {
337 sb.setIndex(0);
338
339 sb.append(preTag);
340 }
341
342 if (tag.hasParameter()) {
343 sb.append("<div class='quote-title'>");
344 sb.append(tag.getParameter() + ":</div>");
345 }
346
347 sb.append("<div class='quote'>");
348 sb.append("<div class='quote-content'>");
349 sb.append(tag.getElement());
350 sb.append("</div></div>");
351 sb.append(postTag);
352
353 html = sb.toString();
354 }
355
356 while ((tag = getFirstTag(html, "size")) != null) {
357 String preTag = html.substring(0, tag.getStartPos());
358 String postTag = html.substring(tag.getEndPos());
359
360 if (sb == null) {
361 sb = new StringBundler(preTag);
362 }
363 else {
364 sb.setIndex(0);
365
366 sb.append(preTag);
367 }
368
369 if (tag.hasParameter()) {
370 Integer size = new Integer(
371 GetterUtil.getInteger(tag.getParameter()));
372
373 if (size.intValue() > 7) {
374 size = new Integer(7);
375 }
376
377 if (fontSizes.containsKey(size)) {
378 sb.append(fontSizes.get(size));
379 sb.append(tag.getElement() + "</span>");
380 }
381 else {
382 sb.append(tag.getElement());
383 }
384 }
385 else {
386 sb.append(tag.getElement());
387 }
388
389 sb.append(postTag);
390
391 html = sb.toString();
392 }
393
394 while ((tag = getFirstTag(html, "url")) != null) {
395 String preTag = html.substring(0, tag.getStartPos());
396 String postTag = html.substring(tag.getEndPos());
397
398 String url = GetterUtil.getString(
399 tag.getParameter(), tag.getElement().trim());
400
401 if (sb == null) {
402 sb = new StringBundler(preTag);
403 }
404 else {
405 sb.setIndex(0);
406
407 sb.append(preTag);
408 }
409
410 sb.append("<a href='");
411 sb.append(url);
412 sb.append("'>");
413 sb.append(tag.getElement());
414 sb.append("</a>");
415 sb.append(postTag);
416
417 html = sb.toString();
418 }
419
420 html = StringUtil.replace(html, "\n", "<br />");
421
422 return html;
423 }
424
425 public static BBCodeTag getFirstTag(String bbcode, String name) {
426 BBCodeTag tag = new BBCodeTag();
427
428 String begTag = "[" + name;
429 String endTag = "[/" + name + "]";
430
431 String preTag = StringUtil.extractFirst(bbcode, begTag);
432
433 if (preTag == null) {
434 return null;
435 }
436
437 if (preTag.length() != bbcode.length()) {
438 tag.setStartPos(preTag.length());
439
440 String remainder = bbcode.substring(
441 preTag.length() + begTag.length());
442
443 int cb = remainder.indexOf("]");
444 int end = _getEndTagPos(remainder, begTag, endTag);
445
446 if (cb > 0 && remainder.startsWith("=")) {
447 tag.setParameter(remainder.substring(1, cb));
448 tag.setElement(remainder.substring(cb + 1, end));
449 }
450 else if (cb == 0) {
451 try {
452 tag.setElement(remainder.substring(1, end));
453 }
454 catch (StringIndexOutOfBoundsException sioobe) {
455 _log.error(bbcode);
456
457 throw sioobe;
458 }
459 }
460 }
461
462 if (tag.hasElement()) {
463 int length =
464 begTag.length() + 1 + tag.getElement().length() +
465 endTag.length();
466
467 if (tag.hasParameter()) {
468 length += 1 + tag.getParameter().length();
469 }
470
471 tag.setEndPos(tag.getStartPos() + length);
472
473 return tag;
474 }
475
476 return null;
477 }
478
479 private static int _getEndTagPos(
480 String remainder, String begTag, String endTag) {
481
482 int nextBegTagPos = remainder.indexOf(begTag);
483 int nextEndTagPos = remainder.indexOf(endTag);
484
485 while ((nextBegTagPos < nextEndTagPos) && (nextBegTagPos >= 0)) {
486 nextBegTagPos = remainder.indexOf(
487 begTag, nextBegTagPos + begTag.length());
488 nextEndTagPos = remainder.indexOf(
489 endTag, nextEndTagPos + endTag.length());
490 }
491
492 return nextEndTagPos;
493 }
494
495 private static String[] _getListItems(String tagElement) {
496 List<String> items = new ArrayList<String>();
497
498 StringBundler sb = new StringBundler();
499
500 int nestLevel = 0;
501
502 for (String item : StringUtil.split(tagElement, "[*]")) {
503 item = item.trim();
504
505 if (item.length() == 0) {
506 continue;
507 }
508
509 int begTagCount = StringUtil.count(item, "[list");
510
511 if (begTagCount > 0) {
512 nestLevel += begTagCount;
513 }
514
515 int endTagCount = StringUtil.count(item, "[/list]");
516
517 if (endTagCount > 0) {
518 nestLevel -= endTagCount;
519 }
520
521 if (nestLevel == 0) {
522 if ((begTagCount == 0) && (endTagCount == 0)) {
523 items.add(item);
524 }
525 else if (endTagCount > 0) {
526 if (sb.length() > 0) {
527 sb.append("[*]");
528 }
529
530 sb.append(item);
531
532 items.add(sb.toString());
533
534 sb.setIndex(0);
535 }
536 }
537 else {
538 if (sb.length() > 0) {
539 sb.append("[*]");
540 }
541
542 sb.append(item);
543 }
544 }
545
546 return items.toArray(new String[items.size()]);
547 }
548
549 private static final String[] _BBCODE_TAGS = {
550 "[b]", "[/b]", "[i]", "[/i]", "[u]", "[/u]", "[s]", "[/s]",
551 "[img]", "[/img]",
552 "[left]", "[center]", "[right]", "[indent]",
553 "[/left]", "[/center]", "[/right]", "[/indent]", "[tt]", "[/tt]"
554 };
555
556 private static final String[] _HTML_TAGS = {
557 "<b>", "</b>", "<i>", "</i>", "<u>", "</u>", "<strike>", "</strike>",
558 "<img alt='' src='", "' />",
559 "<div style='text-align: left;'>", "<div style='text-align: center;'>",
560 "<div style='text-align: right;'>", "<div style='margin-left: 15px;'>",
561 "</div>", "</div>", "</div>", "</div>", "<tt>", "</tt>"
562 };
563
564 private static Log _log = LogFactoryUtil.getLog(BBCodeUtil.class);
565
566 }