001    /**
002     * Copyright (c) 2000-2010 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 (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    }