1   /**
2    * Copyright (c) 2000-2007 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.StringMaker;
27  import com.liferay.portal.kernel.util.StringPool;
28  import com.liferay.portal.kernel.util.StringUtil;
29  import com.liferay.util.Html;
30  
31  import java.util.HashMap;
32  import java.util.Map;
33  
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  
37  /**
38   * <a href="BBCodeUtil.java.html"><b><i>View Source</i></b></a>
39   *
40   * @author Alexander Chow
41   *
42   */
43  public class BBCodeUtil {
44  
45      static Map fontSizes = new HashMap();
46  
47      static Map listStyles = new HashMap();
48  
49      static {
50          fontSizes.put(new Integer(1), "<span style='font-size: 0.7em';>");
51          fontSizes.put(new Integer(2), "<span style='font-size: 0.8em';>");
52          fontSizes.put(new Integer(3), "<span style='font-size: 0.9em';>");
53          fontSizes.put(new Integer(4), "<span style='font-size: 1.0em';>");
54          fontSizes.put(new Integer(5), "<span style='font-size: 1.1em';>");
55          fontSizes.put(new Integer(6), "<span style='font-size: 1.3em';>");
56          fontSizes.put(new Integer(7), "<span style='font-size: 1.5em';>");
57  
58          listStyles.put("1", "<ol style='list-style-type: decimal';>");
59          listStyles.put("i", "<ol style='list-style-type: lower-roman';>");
60          listStyles.put("I", "<ol style='list-style-type: upper-roman';>");
61          listStyles.put("a", "<ol style='list-style-type: lower-alpha';>");
62          listStyles.put("A", "<ol style='list-style-type: upper-alpha';>");
63      }
64  
65      public static final String[][] EMOTICONS = {
66          {"@theme_images_path@/emoticons/angry.gif", ":angry:"},
67          {"@theme_images_path@/emoticons/bashful.gif", ":bashful:"},
68          {"@theme_images_path@/emoticons/big_grin.gif", ":grin:"},
69          {"@theme_images_path@/emoticons/blink.gif", ":blink:"},
70          {"@theme_images_path@/emoticons/blush.gif", ":*)"},
71          {"@theme_images_path@/emoticons/bored.gif", ":bored:"},
72          {"@theme_images_path@/emoticons/closed_eyes.gif", "-_-"},
73          {"@theme_images_path@/emoticons/cold.gif", ":cold:"},
74          {"@theme_images_path@/emoticons/cool.gif", "B)"},
75          {"@theme_images_path@/emoticons/darth_vader.gif", ":vader:"},
76          {"@theme_images_path@/emoticons/dry.gif", "<_<"},
77          {"@theme_images_path@/emoticons/exclamation.gif", ":what:"},
78          {"@theme_images_path@/emoticons/girl.gif", ":girl:"},
79          {"@theme_images_path@/emoticons/glare.gif", ">_>"},
80          {"@theme_images_path@/emoticons/happy.gif", ":)"},
81          {"@theme_images_path@/emoticons/huh.gif", ":huh:"},
82          {"@theme_images_path@/emoticons/in_love.gif", "<3"},
83          {"@theme_images_path@/emoticons/karate_kid.gif", ":kid:"},
84          {"@theme_images_path@/emoticons/kiss.gif", ":#"},
85          {"@theme_images_path@/emoticons/laugh.gif", ":lol:"},
86          {"@theme_images_path@/emoticons/mad.gif", ":mad:"},
87          {"@theme_images_path@/emoticons/mellow.gif", ":mellow:"},
88          {"@theme_images_path@/emoticons/ninja.gif", ":ph34r:"},
89          {"@theme_images_path@/emoticons/oh_my.gif", ":O"},
90          {"@theme_images_path@/emoticons/pac_man.gif", ":V"},
91          {"@theme_images_path@/emoticons/roll_eyes.gif", ":rolleyes:"},
92          {"@theme_images_path@/emoticons/sad.gif", ":("},
93          {"@theme_images_path@/emoticons/sleep.gif", ":sleep:"},
94          {"@theme_images_path@/emoticons/smile.gif", ":D"},
95          {"@theme_images_path@/emoticons/smug.gif", ":smug:"},
96          {"@theme_images_path@/emoticons/suspicious.gif", "8o"},
97          {"@theme_images_path@/emoticons/tongue.gif", ":P"},
98          {"@theme_images_path@/emoticons/unsure.gif", ":unsure:"},
99          {"@theme_images_path@/emoticons/wacko.gif", ":wacko:"},
100         {"@theme_images_path@/emoticons/wink.gif", ":wink:"},
101         {"@theme_images_path@/emoticons/wub.gif", ":wub:"}
102     };
103 
104     public static String getHTML(String bbcode) {
105         String html = Html.escape(bbcode);
106 
107         html = StringUtil.replace(html, _BBCODE_TAGS, _HTML_TAGS);
108 
109         for (int i = 0; i < EMOTICONS.length; i++) {
110             String imgTag = "<img src='" + EMOTICONS[i][0] + "' />";
111 
112             html = StringUtil.replace(html, EMOTICONS[i][1], imgTag);
113         }
114 
115         BBCodeTag tag = null;
116 
117         StringMaker sm = null;
118 
119         while ((tag = getFirstTag(html, "code")) != null) {
120             String preTag = html.substring(0, tag.getStartPos());
121             String postTag = html.substring(tag.getEndPos());
122 
123             String code = tag.getElement().replaceAll("\t", "    ");
124             String[] lines = code.split("\\n");
125             int digits = Integer.toString(lines.length + 1).length();
126 
127             sm = new StringMaker(preTag);
128 
129             sm.append("<div class='message-board-code'>");
130 
131             for (int i = 0; i < lines.length; i++) {
132                 String index = Integer.toString(i + 1);
133                 int ld = index.length();
134 
135                 sm.append("<span class='message-board-code-lines'>");
136 
137                 for (int j = 0; j < digits - ld; j++) {
138                     sm.append("&nbsp;");
139                 }
140 
141                 lines[i] = StringUtil.replace(lines[i], "   ",
142                     StringPool.NBSP + StringPool.SPACE + StringPool.NBSP);
143                 lines[i] = StringUtil.replace(lines[i], "  ",
144                     StringPool.NBSP + StringPool.SPACE);
145 
146                 sm.append(index + "</span>");
147                 sm.append(lines[i]);
148 
149                 if (index.length() < lines.length) {
150                     sm.append("<br />");
151                 }
152             }
153 
154             sm.append("</div>");
155             sm.append(postTag);
156 
157             html = sm.toString();
158         }
159 
160         while ((tag = getFirstTag(html, "color")) != null) {
161             String preTag = html.substring(0, tag.getStartPos());
162             String postTag = html.substring(tag.getEndPos());
163 
164             sm = new StringMaker(preTag);
165 
166             if (tag.hasParameter()) {
167                 sm.append("<span style='color: ");
168                 sm.append(tag.getParameter() + ";'>");
169                 sm.append(tag.getElement() + "</span>");
170             }
171             else {
172                 sm.append(tag.getElement());
173             }
174 
175             sm.append(postTag);
176 
177             html = sm.toString();
178         }
179 
180         while ((tag = getFirstTag(html, "email")) != null) {
181             String preTag = html.substring(0, tag.getStartPos());
182             String postTag = html.substring(tag.getEndPos());
183 
184             String mailto = GetterUtil.getString(
185                 tag.getParameter(), tag.getElement().trim());
186 
187             sm = new StringMaker(preTag);
188 
189             sm.append("<a href='mailto: " + mailto + "'>");
190             sm.append(tag.getElement() + "</a>");
191             sm.append(postTag);
192 
193             html = sm.toString();
194         }
195 
196         while ((tag = getFirstTag(html, "font")) != null) {
197             String preTag = html.substring(0, tag.getStartPos());
198             String postTag = html.substring(tag.getEndPos());
199 
200             sm = new StringMaker(preTag);
201 
202             if (tag.hasParameter()) {
203                 sm.append("<span style='font-family: ");
204                 sm.append(tag.getParameter() + "';>");
205                 sm.append(tag.getElement() + "</span>");
206             }
207             else {
208                 sm.append(tag.getElement());
209             }
210 
211             sm.append(postTag);
212 
213             html = sm.toString();
214         }
215 
216         while ((tag = getFirstTag(html, "img")) != null) {
217             String preTag = html.substring(0, tag.getStartPos());
218             String postTag = html.substring(tag.getEndPos());
219 
220             sm = new StringMaker(preTag);
221 
222             sm.append("<img src='" + tag.getElement().trim() + "' />");
223             sm.append(postTag);
224 
225             html = sm.toString();
226         }
227 
228         while ((tag = getFirstTag(html, "list")) != null) {
229             String preTag = html.substring(0, tag.getStartPos());
230             String postTag = html.substring(tag.getEndPos());
231 
232             String[] items = StringUtil.split(tag.getElement(), "[*]");
233 
234             sm = new StringMaker(preTag);
235 
236             if (tag.hasParameter() &&
237                 listStyles.containsKey(tag.getParameter())) {
238 
239                 sm.append(listStyles.get(tag.getParameter()));
240 
241                 for (int i = 0; i < items.length; i++) {
242                     if (items[i].trim().length() > 0) {
243                         sm.append("<li>" + items[i].trim() + "</li>");
244                     }
245                 }
246 
247                 sm.append("</ol>");
248             }
249             else {
250                 sm.append("<ul style='list-style-type: disc';>");
251 
252                 for (int i = 0; i < items.length; i++) {
253                     if (items[i].trim().length() > 0) {
254                         sm.append("<li>" + items[i].trim() + "</li>");
255                     }
256                 }
257 
258                 sm.append("</ul>");
259             }
260 
261             sm.append(postTag);
262 
263             html = sm.toString();
264         }
265 
266         while ((tag = getFirstTag(html, "quote")) != null) {
267             String preTag = html.substring(0, tag.getStartPos());
268             String postTag = html.substring(tag.getEndPos());
269 
270             sm = new StringMaker(preTag);
271 
272             if (tag.hasParameter()) {
273                 sm.append("<div class='message-board-quote-title'>");
274                 sm.append(tag.getParameter() + ":</div>");
275             }
276 
277             sm.append("<div class='message-board-quote'>");
278             sm.append("<div class='message-board-quote-content'>");
279             sm.append(tag.getElement());
280             sm.append("</div></div>");
281             sm.append(postTag);
282 
283             html = sm.toString();
284         }
285 
286         while ((tag = getFirstTag(html, "size")) != null) {
287             String preTag = html.substring(0, tag.getStartPos());
288             String postTag = html.substring(tag.getEndPos());
289 
290             sm = new StringMaker(preTag);
291 
292             if (tag.hasParameter()) {
293                 Integer size = new Integer(
294                     GetterUtil.getInteger(tag.getParameter()));
295 
296                 if (size.intValue() > 7) {
297                     size = new Integer(7);
298                 }
299 
300                 if (fontSizes.containsKey(size)) {
301                     sm.append(fontSizes.get(size));
302                     sm.append(tag.getElement() + "</span>");
303                 }
304                 else {
305                     sm.append(tag.getElement());
306                 }
307             }
308             else {
309                 sm.append(tag.getElement());
310             }
311 
312             sm.append(postTag);
313 
314             html = sm.toString();
315         }
316 
317         while ((tag = getFirstTag(html, "url")) != null) {
318             String preTag = html.substring(0, tag.getStartPos());
319             String postTag = html.substring(tag.getEndPos());
320 
321             String url = GetterUtil.getString(
322                 tag.getParameter(), tag.getElement().trim());
323 
324             sm = new StringMaker(preTag);
325 
326             sm.append("<a href='" + url + "'>");
327             sm.append(tag.getElement() + "</a>");
328             sm.append(postTag);
329 
330             html = sm.toString();
331         }
332 
333         html = StringUtil.replace(html, "\n", "<br />");
334 
335         return html;
336     }
337 
338     public static BBCodeTag getFirstTag(String bbcode, String name) {
339         BBCodeTag tag = new BBCodeTag();
340 
341         String begTag = "[" + name;
342         String endTag = "[/" + name + "]";
343 
344         String preTag = StringUtil.extractFirst(bbcode, begTag);
345 
346         if (preTag == null) {
347             return null;
348         }
349 
350         if (preTag.length() != bbcode.length()) {
351             tag.setStartPos(preTag.length());
352 
353             String remainder = bbcode.substring(
354                 preTag.length() + begTag.length());
355 
356             int cb = remainder.indexOf("]");
357             int end = remainder.indexOf(endTag);
358 
359             if (cb > 0 && remainder.startsWith("=")) {
360                 tag.setParameter(remainder.substring(1, cb));
361                 tag.setElement(remainder.substring(cb + 1, end));
362             }
363             else if (cb == 0) {
364                 try {
365                     tag.setElement(remainder.substring(1, end));
366                 }
367                 catch (StringIndexOutOfBoundsException sioobe) {
368                     _log.error(bbcode);
369 
370                     throw sioobe;
371                 }
372             }
373         }
374 
375         if (tag.hasElement()) {
376             int length =
377                 begTag.length() + 1 + tag.getElement().length() +
378                     endTag.length();
379 
380             if (tag.hasParameter()) {
381                 length += 1 + tag.getParameter().length();
382             }
383 
384             tag.setEndPos(tag.getStartPos() + length);
385 
386             return tag;
387         }
388 
389         return null;
390     }
391 
392     private static final String[] _BBCODE_TAGS = {
393         "[b]", "[/b]", "[i]", "[/i]", "[u]", "[/u]", "[s]", "[/s]",
394         "[img]", "[/img]",
395         "[left]", "[center]", "[right]", "[indent]",
396         "[/left]", "[/center]", "[/right]", "[/indent]"
397     };
398 
399     private static final String[] _HTML_TAGS = {
400         "<b>", "</b>", "<i>", "</i>", "<u>", "</u>", "<strike>", "</strike>",
401         "<img src='", "' />",
402         "<div style='text-align: left'>", "<div style='text-align: center'>",
403         "<div style='text-align: right'>", "<div style='margin-left: 15px'>",
404         "</div>", "</div>", "</div>", "</div>"
405     };
406 
407     private static Log _log = LogFactory.getLog(BBCodeUtil.class);
408 
409 }