1   /**
2    * Copyright (c) 2000-2008 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portlet.messageboards.util;
24  
25  import com.liferay.portal.kernel.util.GetterUtil;
26  import com.liferay.portal.kernel.util.HtmlUtil;
27  import com.liferay.portal.kernel.util.StringPool;
28  import com.liferay.portal.kernel.util.StringUtil;
29  
30  import java.util.HashMap;
31  import java.util.Map;
32  
33  import org.apache.commons.logging.Log;
34  import org.apache.commons.logging.LogFactory;
35  
36  /**
37   * <a href="BBCodeUtil.java.html"><b><i>View Source</i></b></a>
38   *
39   * @author Alexander Chow
40   *
41   */
42  public class BBCodeUtil {
43  
44      static Map<Integer, String> fontSizes = new HashMap<Integer, String>();
45  
46      static Map<String, String> listStyles = new HashMap<String, String>();
47  
48      static String[][] emoticons = {
49          {"angry.gif", ":angry:"},
50          {"bashful.gif", ":bashful:"},
51          {"big_grin.gif", ":grin:"},
52          {"blink.gif", ":blink:"},
53          {"blush.gif", ":*)"},
54          {"bored.gif", ":bored:"},
55          {"closed_eyes.gif", "-_-"},
56          {"cold.gif", ":cold:"},
57          {"cool.gif", "B)"},
58          {"darth_vader.gif", ":vader:"},
59          {"dry.gif", "<_<"},
60          {"exclamation.gif", ":what:"},
61          {"girl.gif", ":girl:"},
62          {"glare.gif", ">_>"},
63          {"happy.gif", ":)"},
64          {"huh.gif", ":huh:"},
65          {"in_love.gif", "<3"},
66          {"karate_kid.gif", ":kid:"},
67          {"kiss.gif", ":#"},
68          {"laugh.gif", ":lol:"},
69          {"mad.gif", ":mad:"},
70          {"mellow.gif", ":mellow:"},
71          {"ninja.gif", ":ph34r:"},
72          {"oh_my.gif", ":O"},
73          {"pac_man.gif", ":V"},
74          {"roll_eyes.gif", ":rolleyes:"},
75          {"sad.gif", ":("},
76          {"sleep.gif", ":sleep:"},
77          {"smile.gif", ":D"},
78          {"smug.gif", ":smug:"},
79          {"suspicious.gif", "8o"},
80          {"tongue.gif", ":P"},
81          {"unsure.gif", ":unsure:"},
82          {"wacko.gif", ":wacko:"},
83          {"wink.gif", ":wink:"},
84          {"wub.gif", ":wub:"}
85      };
86  
87      static {
88          fontSizes.put(new Integer(1), "<span style='font-size: 0.7em';>");
89          fontSizes.put(new Integer(2), "<span style='font-size: 0.8em';>");
90          fontSizes.put(new Integer(3), "<span style='font-size: 0.9em';>");
91          fontSizes.put(new Integer(4), "<span style='font-size: 1.0em';>");
92          fontSizes.put(new Integer(5), "<span style='font-size: 1.1em';>");
93          fontSizes.put(new Integer(6), "<span style='font-size: 1.3em';>");
94          fontSizes.put(new Integer(7), "<span style='font-size: 1.5em';>");
95  
96          listStyles.put("1", "<ol style='list-style-type: decimal';>");
97          listStyles.put("i", "<ol style='list-style-type: lower-roman';>");
98          listStyles.put("I", "<ol style='list-style-type: upper-roman';>");
99          listStyles.put("a", "<ol style='list-style-type: lower-alpha';>");
100         listStyles.put("A", "<ol style='list-style-type: upper-alpha';>");
101 
102         for (int i = 0; i < emoticons.length; i++) {
103             String[] emoticon = emoticons[i];
104 
105             String image = emoticon[0];
106             String code = emoticon[1];
107 
108             emoticon[0] =
109                 "<img alt='emoticon' src='@theme_images_path@/emoticons/" +
110                     image + "' />";
111             emoticon[1] = HtmlUtil.escape(code);
112         }
113     }
114 
115     public static final String[][] EMOTICONS = emoticons;
116 
117     public static String getHTML(String bbcode) {
118         String html = HtmlUtil.escape(bbcode);
119 
120         html = StringUtil.replace(html, _BBCODE_TAGS, _HTML_TAGS);
121 
122         for (int i = 0; i < emoticons.length; i++) {
123             String[] emoticon = emoticons[i];
124 
125             html = StringUtil.replace(html, emoticon[1], emoticon[0]);
126         }
127 
128         BBCodeTag tag = null;
129 
130         StringBuilder sb = null;
131 
132         while ((tag = getFirstTag(html, "code")) != null) {
133             String preTag = html.substring(0, tag.getStartPos());
134             String postTag = html.substring(tag.getEndPos());
135 
136             String code = tag.getElement().replaceAll(
137                 "\t", StringPool.FOUR_SPACES);
138             String[] lines = code.split("\\n");
139             int digits = String.valueOf(lines.length + 1).length();
140 
141             sb = new StringBuilder(preTag);
142 
143             sb.append("<div class='code'>");
144 
145             for (int i = 0; i < lines.length; i++) {
146                 String index = String.valueOf(i + 1);
147                 int ld = index.length();
148 
149                 sb.append("<span class='code-lines'>");
150 
151                 for (int j = 0; j < digits - ld; j++) {
152                     sb.append("&nbsp;");
153                 }
154 
155                 lines[i] = StringUtil.replace(lines[i], "   ",
156                     StringPool.NBSP + StringPool.SPACE + StringPool.NBSP);
157                 lines[i] = StringUtil.replace(lines[i], "  ",
158                     StringPool.NBSP + StringPool.SPACE);
159 
160                 sb.append(index + "</span>");
161                 sb.append(lines[i]);
162 
163                 if (index.length() < lines.length) {
164                     sb.append("<br />");
165                 }
166             }
167 
168             sb.append("</div>");
169             sb.append(postTag);
170 
171             html = sb.toString();
172         }
173 
174         while ((tag = getFirstTag(html, "color")) != null) {
175             String preTag = html.substring(0, tag.getStartPos());
176             String postTag = html.substring(tag.getEndPos());
177 
178             sb = new StringBuilder(preTag);
179 
180             if (tag.hasParameter()) {
181                 sb.append("<span style='color: ");
182                 sb.append(tag.getParameter() + ";'>");
183                 sb.append(tag.getElement() + "</span>");
184             }
185             else {
186                 sb.append(tag.getElement());
187             }
188 
189             sb.append(postTag);
190 
191             html = sb.toString();
192         }
193 
194         while ((tag = getFirstTag(html, "email")) != null) {
195             String preTag = html.substring(0, tag.getStartPos());
196             String postTag = html.substring(tag.getEndPos());
197 
198             String mailto = GetterUtil.getString(
199                 tag.getParameter(), tag.getElement().trim());
200 
201             sb = new StringBuilder(preTag);
202 
203             sb.append("<a href='mailto: " + mailto + "'>");
204             sb.append(tag.getElement() + "</a>");
205             sb.append(postTag);
206 
207             html = sb.toString();
208         }
209 
210         while ((tag = getFirstTag(html, "font")) != null) {
211             String preTag = html.substring(0, tag.getStartPos());
212             String postTag = html.substring(tag.getEndPos());
213 
214             sb = new StringBuilder(preTag);
215 
216             if (tag.hasParameter()) {
217                 sb.append("<span style='font-family: ");
218                 sb.append(tag.getParameter() + "';>");
219                 sb.append(tag.getElement() + "</span>");
220             }
221             else {
222                 sb.append(tag.getElement());
223             }
224 
225             sb.append(postTag);
226 
227             html = sb.toString();
228         }
229 
230         while ((tag = getFirstTag(html, "img")) != null) {
231             String preTag = html.substring(0, tag.getStartPos());
232             String postTag = html.substring(tag.getEndPos());
233 
234             sb = new StringBuilder(preTag);
235 
236             sb.append("<img alt='' src='" + tag.getElement().trim() + "' />");
237             sb.append(postTag);
238 
239             html = sb.toString();
240         }
241 
242         while ((tag = getFirstTag(html, "list")) != null) {
243             String preTag = html.substring(0, tag.getStartPos());
244             String postTag = html.substring(tag.getEndPos());
245 
246             String[] items = StringUtil.split(tag.getElement(), "[*]");
247 
248             sb = new StringBuilder(preTag);
249 
250             if (tag.hasParameter() &&
251                 listStyles.containsKey(tag.getParameter())) {
252 
253                 sb.append(listStyles.get(tag.getParameter()));
254 
255                 for (int i = 0; i < items.length; i++) {
256                     if (items[i].trim().length() > 0) {
257                         sb.append("<li>" + items[i].trim() + "</li>");
258                     }
259                 }
260 
261                 sb.append("</ol>");
262             }
263             else {
264                 sb.append("<ul style='list-style-type: disc';>");
265 
266                 for (int i = 0; i < items.length; i++) {
267                     if (items[i].trim().length() > 0) {
268                         sb.append("<li>" + items[i].trim() + "</li>");
269                     }
270                 }
271 
272                 sb.append("</ul>");
273             }
274 
275             sb.append(postTag);
276 
277             html = sb.toString();
278         }
279 
280         while ((tag = getFirstTag(html, "quote")) != null) {
281             String preTag = html.substring(0, tag.getStartPos());
282             String postTag = html.substring(tag.getEndPos());
283 
284             sb = new StringBuilder(preTag);
285 
286             if (tag.hasParameter()) {
287                 sb.append("<div class='quote-title'>");
288                 sb.append(tag.getParameter() + ":</div>");
289             }
290 
291             sb.append("<div class='quote'>");
292             sb.append("<div class='quote-content'>");
293             sb.append(tag.getElement());
294             sb.append("</div></div>");
295             sb.append(postTag);
296 
297             html = sb.toString();
298         }
299 
300         while ((tag = getFirstTag(html, "size")) != null) {
301             String preTag = html.substring(0, tag.getStartPos());
302             String postTag = html.substring(tag.getEndPos());
303 
304             sb = new StringBuilder(preTag);
305 
306             if (tag.hasParameter()) {
307                 Integer size = new Integer(
308                     GetterUtil.getInteger(tag.getParameter()));
309 
310                 if (size.intValue() > 7) {
311                     size = new Integer(7);
312                 }
313 
314                 if (fontSizes.containsKey(size)) {
315                     sb.append(fontSizes.get(size));
316                     sb.append(tag.getElement() + "</span>");
317                 }
318                 else {
319                     sb.append(tag.getElement());
320                 }
321             }
322             else {
323                 sb.append(tag.getElement());
324             }
325 
326             sb.append(postTag);
327 
328             html = sb.toString();
329         }
330 
331         while ((tag = getFirstTag(html, "url")) != null) {
332             String preTag = html.substring(0, tag.getStartPos());
333             String postTag = html.substring(tag.getEndPos());
334 
335             String url = GetterUtil.getString(
336                 tag.getParameter(), tag.getElement().trim());
337 
338             sb = new StringBuilder(preTag);
339 
340             sb.append("<a href='" + url + "'>");
341             sb.append(tag.getElement() + "</a>");
342             sb.append(postTag);
343 
344             html = sb.toString();
345         }
346 
347         html = StringUtil.replace(html, "\n", "<br />");
348 
349         return html;
350     }
351 
352     public static BBCodeTag getFirstTag(String bbcode, String name) {
353         BBCodeTag tag = new BBCodeTag();
354 
355         String begTag = "[" + name;
356         String endTag = "[/" + name + "]";
357 
358         String preTag = StringUtil.extractFirst(bbcode, begTag);
359 
360         if (preTag == null) {
361             return null;
362         }
363 
364         if (preTag.length() != bbcode.length()) {
365             tag.setStartPos(preTag.length());
366 
367             String remainder = bbcode.substring(
368                 preTag.length() + begTag.length());
369 
370             int cb = remainder.indexOf("]");
371             int end = remainder.indexOf(endTag);
372 
373             if (cb > 0 && remainder.startsWith("=")) {
374                 tag.setParameter(remainder.substring(1, cb));
375                 tag.setElement(remainder.substring(cb + 1, end));
376             }
377             else if (cb == 0) {
378                 try {
379                     tag.setElement(remainder.substring(1, end));
380                 }
381                 catch (StringIndexOutOfBoundsException sioobe) {
382                     _log.error(bbcode);
383 
384                     throw sioobe;
385                 }
386             }
387         }
388 
389         if (tag.hasElement()) {
390             int length =
391                 begTag.length() + 1 + tag.getElement().length() +
392                     endTag.length();
393 
394             if (tag.hasParameter()) {
395                 length += 1 + tag.getParameter().length();
396             }
397 
398             tag.setEndPos(tag.getStartPos() + length);
399 
400             return tag;
401         }
402 
403         return null;
404     }
405 
406     private static final String[] _BBCODE_TAGS = {
407         "[b]", "[/b]", "[i]", "[/i]", "[u]", "[/u]", "[s]", "[/s]",
408         "[img]", "[/img]",
409         "[left]", "[center]", "[right]", "[indent]",
410         "[/left]", "[/center]", "[/right]", "[/indent]", "[tt]", "[/tt]"
411     };
412 
413     private static final String[] _HTML_TAGS = {
414         "<b>", "</b>", "<i>", "</i>", "<u>", "</u>", "<strike>", "</strike>",
415         "<img alt='' src='", "' />",
416         "<div style='text-align: left'>", "<div style='text-align: center'>",
417         "<div style='text-align: right'>", "<div style='margin-left: 15px'>",
418         "</div>", "</div>", "</div>", "</div>", "<tt>", "</tt>"
419     };
420 
421     private static Log _log = LogFactory.getLog(BBCodeUtil.class);
422 
423 }