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