001    /**
002     * Copyright (c) 2000-2011 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
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    /**
032     * @author Alexander Chow
033     */
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("&nbsp;");
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 (end > 0) {
445                                    if (cb > 0 && remainder.startsWith("=")) {
446                                            tag.setParameter(remainder.substring(1, cb));
447                                            tag.setElement(remainder.substring(cb + 1, end));
448                                    }
449                                    else if (cb == 0) {
450                                            try {
451                                                    tag.setElement(remainder.substring(1, end));
452                                            }
453                                            catch (StringIndexOutOfBoundsException sioobe) {
454                                                    _log.error(bbcode);
455    
456                                                    throw sioobe;
457                                            }
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    }